Skip to content

Fix DatabricksSqlHook crash on empty result with fetch_one handler#69230

Merged
eladkal merged 3 commits into
apache:mainfrom
moomindani:fix-databricks-sql-none-result
Jul 2, 2026
Merged

Fix DatabricksSqlHook crash on empty result with fetch_one handler#69230
eladkal merged 3 commits into
apache:mainfrom
moomindani:fix-databricks-sql-none-result

Conversation

@moomindani

Copy link
Copy Markdown
Contributor

Fixes a TypeError in DatabricksSqlHook.run() when a scalar handler is used and the query returns no rows.

DBAPI fetchone() returns None on an empty result set, and common.sql's fetch_one_handler is typed -> tuple | None accordingly (it also returns None for statements where cursor.description is None). The base DbApiHook._make_common_data_structure passes None through unchanged, and DbApiHook.run() is annotated ... | None. The Databricks override, however, only accepted Row or Sequence[Row] and raised TypeError: Expected Sequence[Row] or Row, but got <class 'NoneType'>.

User-visible impact: DatabricksSqlHook.get_first(...) — which is just run(handler=fetch_one_handler) — crashed on any query returning no rows, where every non-overriding DbApiHook (postgres, snowflake, ...) returns None. With this change the Databricks hook follows the common.sql contract and passes None through.

(The odbc hook's override maps falsy results to [] instead; None is deliberately chosen here to preserve fetchone semantics — "no row" and "empty row set" stay distinguishable — and to match the base contract.)

Found while validating #39448 against a live Databricks SQL warehouse; verified there that with this change run(..., handler=fetch_one_handler) on an empty result returns None and serializes cleanly through XCom.

related: #39448


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Fable 5)

Generated-by: Claude Code (Fable 5) following the guidelines

…_one handler

DBAPI fetchone() returns None on an empty result set, but the hook's
_make_common_data_structure only accepted Row objects or lists of them,
so run() with a scalar handler crashed on empty results instead of
returning None as the common.sql contract and other providers do.
The base implementation passes the handler result through unchanged, and
scalar handlers such as fetch_one_handler legitimately return None for
empty result sets, so the annotation excluded a value the method already
returns. Mypy's override check rejected the Databricks subclass returning
None against the narrower supertype annotation.
…esults

These hooks copy the base run() implementation, so their returned result
lists can also contain None once the base _make_common_data_structure
annotation admits it; mypy rejected the narrower list element type.
@moomindani
moomindani requested a review from potiuk as a code owner July 2, 2026 05:41
@eladkal
eladkal merged commit 81dbef9 into apache:main Jul 2, 2026
85 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants