[SQL] Fusion does not flag an un-ref()'d relation; bare name silently binds to a same-named table and builds an empty table reported as success
- Dominant language
- Rust
- Stars
- 13.8k
- Forks
- 2.6k
- Avg merge
- 21h 31m
- Merged PRs (30d)
- 56
Description
### Before you submit
- [x] I have searched the existing issues and could not find a duplicate
### SQL that dbt Fusion doesn't understand
```sql
with
cte_items as (
select order_id, sku from {{ ref('raw_items') }}
),
-- The ref() was forgotten: `raw_stores` is written as a bare name. It is not a ref(),
-- source, or CTE, but a table of that name exists in the target schema, so Fusion resolves
-- it silently and creates no lineage edge / no warning. Its `id` never equals an item `sku`.
orders_with_activity as (
select distinct cte_items.order_id
from cte_items
inner join raw_stores on raw_stores.id = cte_items.sku
)
select * from orders_with_activity
```
### What dbt Fusion does
```shell
# render + analyze both pass, then the warehouse runs it and the model succeeds empty
Finished rendering model ... repro_empty_via_raw_seed [success]
Finished analyzing model ... repro_empty_via_raw_seed [success]
Succeeded [ 1.27s] model AAAA_SELFHOSTED.repro_empty_via_raw_seed (table)
Summary: 1 total | 1 success
# No warning or error is raised about the bare `raw_stores` reference.
# The compiled SQL qualifies the ref()'d relations but leaves the forgotten one bare:
# from analytics.AAAA_SELFHOSTED.raw_items <- ref(), schema-qualified
# inner join raw_stores ... <- forgotten ref, left bare
# raw_stores.id never equals an item sku -> the table is built with 0 rows.
```
Net effect: a forgotten `ref()` produces a silently empty table and a successful run.
### What you expected
Fusion's static analysis builds a schema store / catalog and resolves column lineage, so it has the information to detect this before execution. A relation that is neither a `ref()`, a `source()`, nor a CTE — particularly when a node or object of that name exists in the project or schema — should be **surfaced at compile/analyze time** (warning or error), rather than silently bound to an arbitrary same-named object at runtime. The forgotten ref should not be able to ship an empty production table under a successful run.
### What kind of SQL understanding issue is this?
- Wrong column lineage / ref resolution
### Which database adapter are you using?
- snowflake
### Does this SQL execute correctly with `--static-analysis off`, or when run by dbt Core?
- [x] Yes — this SQL is accepted by the warehouse (the bare name resolves to an existing object; the run succeeds and builds an empty table)
### Environment
```markdown
- dbt Fusion version: 2.0.0-preview.186 and 2.0.0-preview.193 (`dbt --version`)
- adapter: dbt-snowflake (v2)
- OS: macOS (darwin, aarch64)
```
### Additional Context
- **Behavior is identical on `2.0.0-preview.186` and the latest `2.0.0-preview.193`** (verified via `dbt system update` then re-run) — not a transient preview regression.
- dbt core fails with a database error (`dbt=1.11.0-b3`)
```
13:34:28 Failure in model repro_empty_via_raw_seed (models/repro_cte_pruning/repro_empty_via_raw_seed.sql)
13:34:28 Database Error in model repro_empty_via_raw_seed (models/repro_cte_pruning/repro_empty_via_raw_seed.sql)
002003 (42S02): SQL compilation error:
Object 'ANALYTICS.AAAA_SELFHOSTED.RAW_ORDERS' does not exist or not authorized.
compiled code at target/run/jaffle_shop/models/repro_cte_pruning/repro_empty_via_raw_seed.sql
13:34:28
13:34:28 compiled code at target/compiled/jaffle_shop/models/repro_cte_pruning/repro_empty_via_raw_seed.sql
```
- Deterministic: re-seeding the bare-referenced object with `--full-refresh` and rebuilding repeatedly yields the same silent success, so it is not a caching/stale-state artifact.
Contributor guide
Assessment
This issue has not been assessed yet.