Repository navigation
feat(context): warn when load_result returns None during parse phase (#11070) - #12815
SarthakB11 wants to merge 2 commits into
Conversation
|
Thanks for your pull request, and welcome to our community! We require contributors to sign our Contributor License Agreement and we don't seem to have your signature on file. Check out this article for more information on why we have a CLA. In order for us to review and merge your code, please submit the Individual Contributor License Agreement form attached above above. If you have questions about the CLA, or if you believe you've received this message in error, please reach out through a comment on this PR. CLA has not been signed by users: @SarthakB11 |
…abs#11070) When a dbt user calls an introspective helper like `run_query` or `load_result` from a model body without wrapping it in an `{% if execute %}` block, the result silently comes back as None during the parse phase. The failure then surfaces as the cryptic Compilation Error in model ... 'None' has no attribute 'table' which is hard to debug unless you already know about parse vs execute phases. This patch extends `ProviderContext.load_result` so that when it is about to return None for a name that was never stored, and the provider is in parse mode (`self.provider.execute is False`), it fires a `JinjaLogWarning` that points the user at the `execute` docs. Execute-mode callers are unaffected, since a missing name there is a real user bug (typo on the name, or calling `load_result` on a name never wrapped in a `statement`). The warning uses the existing `fire_event(JinjaLogWarning(msg=...))` pattern already used in `store_seed_results` a few hundred lines below, so no new imports are needed. Adds a changelog entry under `.changes/unreleased/` describing the new warning. No behaviour change for existing execute-mode calls. Smoke-tested the branch logic with a minimal fake ProviderContext (parse + unknown name -> warns, execute + unknown name -> no warn). Fixes dbt-labs#11070
369b0f1 to
e64494d
Compare
|
@QMalcolm — would you be the right person to review here? It's a small change in |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12815 +/- ##
==========================================
- Coverage 91.29% 91.26% -0.03%
==========================================
Files 203 203
Lines 25916 25919 +3
==========================================
- Hits 23659 23654 -5
- Misses 2257 2265 +8
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
QMalcolm
left a comment
There was a problem hiding this comment.
Hi @SarthakB11, thank you for doing this work ❤️ The changes in providers.py looks good to me.
The one thing we should add before merging is a test. We have a utility, EventCatcher, which is useful for testing changes like this. Here is an example test: https://github.com/dbt-labs/dbt-core/blob/c02340d4c14df1459c00cf91b9ab738e1c4c9507/tests/functional/cli/test_requires.py#L55
|
We can safely ignore the Enforce Additional Reviews on Artifact and Validations Changes / Validate Additional Reviews check here |
|
Pushed 347a002 with the test using Quick note on scope: the providers.py fix catches |
|
Closing this — dbt-core's main branch is now the Rust rewrite (dbt Core v2.0), and core/dbt/context/providers.py (what this PR touches) no longer exists there. The Python v1 codebase moved to main_backup, which was frozen June 1 and isn't taking further merges, and CONTRIBUTING.md now directs all new community PRs to the Rust main. Thanks for the review earlier, @QMalcolm — the underlying gap (silent None from load_result during parse) may still be worth a look if it has a Rust-side equivalent, but that'd need a fresh PR against the current codebase rather than a rebase of this one. |
Fixes #11070.
What
Extends
ProviderContext.load_resultincore/dbt/context/providers.pyso that when it's about to returnNonefor a name that was never stored, and the provider is in parse mode (self.provider.execute is False), it fires aJinjaLogWarningpointing the user at the{% if execute %}docs.Why
Quoting @joellabes on the issue:
and the alternative suggestion:
@graciegoheen on 2026-03-10:
(Also noting that #12736, a prior attempt, was self-closed by @claygeo on 2026-03-30 for queue management, not on merits — so the direction is still open.)
Where
The
run_querymacro path is:{% set r = run_query(sql) %}in a model.run_query(defined in dbt-adapters) calls{% call statement("run_query_statement", ...) %}and thenload_result("run_query_statement").executeis False,statement()'s body is skipped, nothing is stored.load_result("run_query_statement")drops into theelsebranch ofProviderContext.load_resultin dbt-core and returnsNone.None.table/None.columns→ the cryptic compile error.That
elsebranch is a narrow, dbt-core-owned choke point that sees everyrun_querycall during parse, which is why it's a better hook than trying to patchrun_queryorstatementin dbt-adapters.Diff summary
Uses the same
fire_event(JinjaLogWarning(msg=...))pattern already used instore_seed_resultsa few hundred lines below, so no new imports are needed.Behaviour
Nonesilently. A missing name in execute mode is a real user bug (typo, or callingload_resulton a name never wrapped in astatementblock) and should not be converted into a spurious "wrap in execute" warning.sql_results→ completely unchanged.Tests
tests/unit/context/test_context.pyonly validates the set of keys available on the context, and there's no existingload_resultbehavioral test harness. Adding one would require spinning up a fullProviderContextwith model fixtures; happy to add that in a follow-up if a maintainer prefers.ProviderContext(parse + unknown name → warns; execute + unknown name → no warn;_sql_results-present branches unchanged).python -m py_compile core/dbt/context/providers.pypasses.Changelog
Added
.changes/unreleased/Features-20260411-184500.yamlvia the standard changie format.