Uh oh!
There was an error while loading. Please reload this page.
Validate DatabricksSQLStatementsSensor exclusivity at __init__ - #70831
Conversation
ahilashsasidharan
commented
Jul 31, 2026
Question: Could the AirflowException for both checks mentioned in this PR be converted to ValueError raises as part of this PR or should they remain as is? |
potiuk
commented
Aug 1, 2026
This gets the split right, which is worth saying explicitly because several PRs in this series have not. The "both provided" case is a provision check — passing both Leaving the "neither resolves" case in Tests are split to match, which reads well. One thing before merge. The relocated line still raises ifstatementisnotNoneandstatement_idisnotNone:
raiseAirflowException("Cannot provide both statement and statement_id.")Moving one verbatim during a refactor is explicitly allowed, and the file's ratchet entry stays at ifstatementisnotNoneandstatement_idisnotNone:
raiseValueError("Cannot provide both statement and statement_id.")That needs the test's Happy to merge once that's in. Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting |
shahar1
left a comment
There was a problem hiding this comment.
The split is right and the follow-up commit lands everything that was asked for — ValueError, the updated pytest.raises, and the ratchet drop to ::3. Two observations inline and one process note below; none are blockers.
I checked a few things rather than assuming them, and they hold up:
- #70340 (which moved the check into
execute()) landed after the last provider release prep, so it is unreleased. Against released 7.18.0 this PR is a net no-op on placement — the genuinely new behaviour is the polarity and the exception type, which matches how the description frames it. - Pre-#70340 the
__init__check wasif statement and statement_idraisingAirflowException. So this really isn't a straight revert, as stated. DatabricksSQLStatementsOperatortakesstatement: stras required and does not list it intemplate_fields, so there is no parallel site needing the same treatment. Nothing sibling is left half-fixed.
Process — the PR title ships into the changelog verbatim
The title currently ends in a literal ellipsis:
Rewrite DatabricksSQLStatementsSensor exclusivity check and move back…
Databricks changelogs are regenerated from git log by the release manager rather than from newsfragments (see providers/AGENTS.md), so the truncation would land as-is in providers/databricks/docs/changelog.rst. Worth retitling to something that stands on its own and stays under 70 characters — for example:
Validate DatabricksSQLStatementsSensor exclusivity at
__init__
Please retitle the commit as well as the PR, since the commit message is what the changelog is generated from.
On your question about the remaining AirflowExceptions
Could the AirflowException for both checks mentioned in this PR be converted to ValueError raises as part of this PR or should they remain as is?
Both readings are defensible, and the ratchet permits either. Neither warehouse_id must be provided. nor One of either statement or statement_id must be provided. is modified by this diff, so leaving them is not a violation — the check-no-new-airflow-exceptions hook only guards new usages.
That said, I'd convert them here. The same "you're already touching this function" argument that applied to the relocated line applies to its two neighbours, and the current state leaves an odd surface for anyone writing an except block: passing both statement and statement_id raises ValueError, while passing neither raises AirflowException, for what a Dag author experiences as the same class of mistake. Both are argument-provision errors and ValueError fits both. That would take the ratchet entry from ::3 to ::1.
If you'd rather keep this PR tightly scoped to what was asked for, a follow-up is fine too — just say which way you're going so the ratchet line doesn't get churned twice.
This review was drafted by an AI-assisted tool and
confirmed by an Apache Airflow maintainer. The findings
below are observations, not blockers; an Apache Airflow
maintainer — a real person — will take the next look at the
PR. If you think a finding is mis-applied, please reply on
the PR and a maintainer will weigh in.More on how Apache Airflow handles maintainer review:
contributing-docs/05_pull_requests.rst.
Drafted-by: Claude Code (Opus 5); reviewed by @shahar1 before posting
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
191aecc to
6e1cf21Compareahilashsasidharan
commented
Aug 3, 2026
I’ve updated the PR title and rewritten the first commit message to the suggested form. My understanding is that this is the commit whose message is used when generating the provider changelog, so that’s the one I corrected.
I’ve opted to make the additional changes in this PR. Since the diff already touches this function, it felt cleaner to apply the consistent ValueError treatment now rather than opening a follow‑up PR. |
ahilashsasidharan
commented
Aug 3, 2026
Follow up question for some of my related commits in this series:
|
ahilashsasidharan
commented
Aug 7, 2026
@shahar1 pinging since this should be ready for a re-review when you have the chance |
eladkal
commented
Aug 17, 2026
cc @moomindani for review |
moomindani
left a comment
There was a problem hiding this comment.
Thanks for the clear write-up — the reasoning matches the criterion in #70503 exactly, and it follows the same restoration already merged for the Repos operators in #70551.
I ran a differential against a live SQL warehouse. Provider 7.18.1 already contains #70340, so the unpatched wheel reproduces current main and the patched one carries your two edits (Airflow 3.2.2, real warehouse_id, real statements executed):
| case | current main | with this PR |
|---|---|---|
statement + templated statement_id rendering to None (render_template_as_native_obj=True) | task succeeds — the contradictory pair is silently ignored and the statement runs | Dag import error |
statement="SELECT 1", statement_id="" | task succeeds against the warehouse | Dag import error |
statement="", statement_id="" | execute(): "One of either statement or statement_id must be provided." | __init__: "Cannot provide both statement and statement_id." |
statement only / statement_id only | constructed | constructed |
The first row is an argument for this change that the description does not make: on main, supplying both arguments produces no error at all once one of them renders to None, and the sensor quietly runs the other. Catching that at parse time is a real improvement rather than a relocation — worth saying in the description.
The second row is a user-visible behaviour change: statement_id="" alongside a statement works today (confirmed end to end) and becomes a Dag import error here. Your parametrization shows it is deliberate, so this is only about making it visible to whoever reads the changelog later.
Nits inline, none blocking.
Drafted-by: Claude Code (Opus 5); reviewed by @moomindani before posting
| **kwargs, | ||
| ): | ||
| # Handle the scenario where either both statement and statement_id are set/not set | ||
| if statement is not None and statement_id is not None: |
There was a problem hiding this comment.
With both fields "" this raises "Cannot provide both" even though nothing was really provided — measured on the patched build, and main reports the accurate "One of either statement or statement_id must be provided." from execute() for the same input.
The is not None polarity is right and I would not weaken it; the cheapest fix is a message that holds for either failure, e.g. "Provide exactly one of statement or statement_id."
| ({"statement": STATEMENT, "statement_id": STATEMENT_ID}, "Cannot provide both"), | ||
| ({}, "One of either statement or statement_id"), | ||
| (STATEMENT, STATEMENT_ID), | ||
| (STATEMENT, ""), |
There was a problem hiding this comment.
Worth adding ("", "") here so whichever behaviour you settle on for the both-empty case is pinned down — right now nothing covers it.
| ("statement", "statement_id"), | ||
| [ | ||
| (None, None), | ||
| ("", None), |
There was a problem hiding this comment.
The case that actually motivates the polarity change is not covered anywhere: a provided field that renders to None under render_template_as_native_obj=True, which must not slip past the exclusivity check. Without it, a later well-meaning move back to a truthiness check in execute() would pass CI.
eladkal
commented
Aug 24, 2026
@ahilashsasidharan can you fix the open items? |
Sorry for the delay. Will work on these |
Description
This PR moves template_field exclusivity check back to
__init__fromexecute()while keeping thenot statement and not statement_idsibling inexecute()since it is a required-argument check (see #70503) for theDatabricksSQLStatementsSensoroperators in the providers/databricks/src/airflow/providers/databricks/sensors/databricks.py.The check is updated to use
is not Nonepolarity rather than a truthiness check, so this is not a straight revert of the original code.These are pure provision checks ("was this argument passed?") which belong in
__init__per the updated guidance in #70296, as moving them to execute() risks false positives whenrender_template_as_native_obj=Truerenders a provided field to None.Following the logic of "you're already touching this function" argument the PR also converts 3 AirflowExceptions to ValueErrors under both
__init__andexecute()to increase consistency and follow guidance to narrow when changing a line.Tests
is not Nonepolarity is used in__init__while theis Nonepolarity is not used inexecute()for the sibling testcase.related: #70296 and #70503
Was generative AI tooling used to co-author this PR?
Generated-by: [Antigravity IDE] following the guidelines
{pr_number}.significant.rst, in airflow-core/newsfragments. You can add this file in a follow-up commit after the PR is created so you know the PR number.