Skip to content

fix(planner): observe source freshness per source and fail plans on missing source tables - #908

Merged
kvlonge merged 3 commits into
mainfrom
fix/plan-source-freshness-isolation
Oct 1, 2026
Merged

kvlonge merged 3 commits into
mainfrom
fix/plan-source-freshness-isolation

Conversation

@kvlonge

@kvlonge kvlonge commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Why

During sqb plan, adapter source freshness was fetched as one batch. If any one source table had no metadata, every source in the batch became "freshness unknown", and the only output was Freshness metadata FAIL while the plan still succeeded. On a real ~260-source Snowflake project, three missing tables hid freshness for every source on every plan (CHI-464).

Changes

  • Freshness metadata is reported per source; one missing or unavailable table affects only that source. Progress lines state counts and reasons; warnings are grouped by reason; sqb plan --json adds unknown_source_details; sqb freshness reports per source.
  • New S405: a selected model reads a source table that does not exist. Sources missing from the existing listing are probed by the exact rendered name (bounded concurrency); only the adapter's not-found errors count, and Snowflake's ambiguous "does not exist or not authorized" names the current role. Managed, loader and expression sources are skipped.
  • Databricks falls back to Unity Catalog LAST_ALTERED when Delta history has no timestamp.

Verification

  • DuckDB E2E: missing table fails plan and build naming it; a case-variant declaration plans; other sources keep freshness; per-source sqb freshness.
  • Offline adapter tests for probe classification, Databricks catalog-less sources, SQL Server cross-database, Postgres materialized views and Snowflake streams.
  • Real Snowflake prod plan (read-only): 257 sources, 0 unknown.
  • make test, make check-ci; one independent review plus a follow-up.

… source tables

Adapter freshness metadata is still fetched in one batched lookup, but each
table now gets its own outcome (observed, missing, or unavailable). A missing
table, a view, or a null last-modified time no longer makes every source in
the batch unknown.

Planning fails with S405 when a selected resource reads an external source
table that does not exist, naming the source, its table, the reading models,
and the declaration file and line. Managed, loader, and expression sources
are exempt.

The Freshness metadata progress line reports source and unknown counts, a
failed freshness query explains itself, and the plan carries a warning and
JSON details for each unknown-freshness reason. sqb freshness reports each
source on its own.

Refs: CHI-464
S405 now treats the relation listing as a first pass only. It reuses the
listing already gathered for source columns, then probes each source the
listing did not find with the exact relation name the model SQL reads.
Only the adapter's object-not-found error counts as missing; other probe
errors fail planning as themselves. Each first-class adapter classifies its
own not-found errors.

Adapter freshness metadata no longer reports "missing": a table the
metadata does not describe is unavailable, since only the probe can decide
existence. Databricks again falls back to Unity Catalog when Delta history
has no timestamp. Freshness warnings group by reason, every unknown source
has a detail entry, and the S405 docs cover the probe and pre_hook tables.

Refs: CHI-464
The relation read probe now returns readable, missing, or missing-or-
unreadable. Snowflake's 2003/2043 errors cannot tell a missing table from
one the role cannot read, so S405 says "does not exist or is not readable",
names the role when the connection reports it, and suggests granting
SELECT as well as creating the table.

Databricks schema and catalog not-found errors and DuckDB missing attached
catalogs now count as missing. Probes for sources the listing did not find
run through the shared bounded inspection pool.

The SQL test build parity fixture plans against a warehouse that holds its
source table.

Refs: CHI-464
@kvlonge
kvlonge enabled auto-merge (squash) October 1, 2026 22:02
@kvlonge
kvlonge merged commit d79e9b7 into main Oct 1, 2026
32 checks passed
@kvlonge
kvlonge deleted the fix/plan-source-freshness-isolation branch October 1, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant