Actual run · a read-only warehouse + a writable OneLake lakehouse + a local catalog · 2026-06-22 UTC. A Fabric Warehouse and a Lakehouse
are the same thing to duckrun — Delta in OneLake — except the warehouse is locked to writes.
conn.attach(...) binds them (plus a local folder) as catalogs; every table is named
catalog.schema.table, and a single conn.sql joins across all three. Tokens are
automatic on OneLake — no credentials in the code.
A writable lakehouse (primary), a read-only warehouse, and a local folder — bound with conn.attach. On OneLake the token is automatic (nothing to pass in a Fabric notebook).
# a Warehouse is just a Lakehouse that is locked to writes — attach it read-only. # OneLake tokens are acquired automatically (Fabric notebook / env / az login): conn.attach(warehouse_path, name="warehouse", schema="mart", read_only=True) conn.attach(local_path, name="local") # a plain local folder, writable
conn.catalog.listCatalogs()
| catalog | mode | storage |
|---|---|---|
| lakehouse | primary | OneLake (abfss) |
| warehouse | read-only | OneLake (abfss) |
| local | writable | local |
three catalogs bound to one session; 'lakehouse' is writable, 'warehouse' is read-only
# one session sees all three catalogs; every table has a three-part name:
conn.sql("""SELECT table_catalog, table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema','pg_catalog','main')
ORDER BY 1, 2, 3""")
every table the session can see, three-part named
| catalog | fully-qualified name (catalog.schema.table) |
|---|---|
| lakehouse | lakehouse.mart.dim_duid |
| lakehouse | lakehouse.mart.fct_summary |
| lakehouse | lakehouse.mart.xxxx |
| warehouse | warehouse.mart.dim_calendar |
| warehouse | warehouse.mart.dim_duid |
| warehouse | warehouse.mart.fct_summary |
6 tables across 3 catalogs — the warehouse and lakehouse expose the SAME mart tables; only the warehouse is read-only
# 3-part — fully qualified, works from anywhere (cross-catalog):
conn.sql("SELECT * FROM warehouse.mart.fct_summary LIMIT 5")
# 2-part — schema.table, in the current catalog:
conn.catalog.setCurrentCatalog("warehouse"); conn.sql("SELECT * FROM mart.fct_summary LIMIT 5")
# 1-part — bare table, in the current catalog + database:
conn.catalog.setCurrentDatabase("mart"); conn.sql("SELECT * FROM fct_summary LIMIT 5")
the same warehouse table, three naming conventions — identical on a bounded probe
| naming | reference | rows (100k sample probe) |
|---|---|---|
| 3-part | warehouse.mart.fct_summary | 100,000 |
| 2-part | mart.fct_summary (current catalog) | 100,000 |
| 1-part | fct_summary (current catalog + db) | 100,000 |
all three names resolve to the same table (probed on a 100k-row sample, not the full fact)
conn.sql("SELECT 1 AS x").write.mode("overwrite").saveAsTable("warehouse.mart.nope")
# -> PermissionError: catalog 'warehouse' is read-only
the same write, two catalogs
| catalog | mode | write attempt |
|---|---|---|
| warehouse | read-only | refused ✅ |
| lakehouse | writable | allowed ✅ |
the warehouse refuses writes (locked); the lakehouse accepts them
Facts from the warehouse, the DUID dimension from the lakehouse, a carbon-factor lookup from the local catalog — joined in one query and written back to the lakehouse.
lookup = conn.sql("SELECT * FROM (VALUES …) t(fuel, co2_kg_per_mwh)")
lookup.write.mode("overwrite").saveAsTable("local.dbo.fuel_factors")
15 fuel factors written to the local catalog
# facts: WAREHOUSE (read-only) · DUID dim: LAKEHOUSE · carbon factor: LOCAL
mart = conn.sql(f"""
WITH facts AS ( -- a bounded sample, not the whole 100M+-row fact
SELECT DUID, mw, price FROM warehouse.mart.fct_summary LIMIT 200000
)
SELECT d.State,
round(sum(f.mw), 0) AS total_mw,
round(avg(f.price), 2) AS avg_price,
round(sum(f.mw * coalesce(lf.co2_kg_per_mwh,0))/1000.0, 1) AS est_tonnes_co2
FROM facts f
JOIN mart.dim_duid d ON d.DUID = f.DUID
LEFT JOIN local.dbo.fuel_factors lf ON lower(lf.fuel) = lower(d.FuelSourceDescriptor)
GROUP BY d.State""")
mart.write.mode("overwrite").saveAsTable("multicatalog_demo.mart_generation_by_state")
multicatalog_demo.mart_generation_by_state — warehouse ⋈ lakehouse ⋈ local (200,000-row fact sample)
| state | total MW | avg price $ | est t CO₂ |
|---|---|---|---|
| Queensland | 2,927,844 | 104.94 | 644,304 |
| Victoria | 2,253,936 | 98.41 | 433,265 |
| New South Wales | 2,071,641 | 98.83 | 451,617 |
| South Australia | 995,708 | 128.82 | 447,044 |
| Tasmania | 747,277 | 84.74 | 43,236 |
mart written back to the lakehouse (abfss://…/Tables/multicatalog_demo/mart_generation_by_state) — joined a 200,000-row fact sample(warehouse) + dim(lakehouse) + factors(local)