Conversation
…s (SC-121461) `get_engine_spec(backend, driver)` matches a driver exactly first, but when a stored Databricks SQLAlchemy URI carries a driver suffix that matches no registered spec (e.g. a legacy/hand-written suffix, or one saved before the `databricks-sql-python` key existed) it falls back to the *first* backend-matching spec in module-definition order. `DatabricksHiveEngineSpec` was the first concrete Databricks spec defined, so that ambiguous fallback selected it — even for URIs that actually connect via `databricks.sql.client.Cursor`. Its inherited `HiveEngineSpec.execute()` unconditionally passes an `async` kwarg, which the real Databricks cursor (no `**kwargs` catch-all) rejects with `TypeError: Cursor.execute() got an unexpected keyword argument 'async'`. This fires on every table-metadata lookup via `DatabaseRestApi.table_extra_metadata` -> `get_create_view`. Reorder the concrete Databricks engine specs so the general-purpose `DatabricksPythonConnectorEngineSpec` is defined first and therefore wins the ambiguous-driver fallback, with the legacy ODBC/Native/Hive specs after it. Pure relocation of class definitions plus explanatory comments documenting the ordering invariant; no logic changes. Exact driver matches are unaffected. Fixes SUPERSET-PYTHON-179V Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code Review Agent Run #9b0bb9Actionable Suggestions - 0Additional Suggestions - 1
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #44449 +/- ##
==========================================
- Coverage 80.65% 80.65% -0.01%
==========================================
Files 2942 2942
Lines 175692 175692
Branches 40788 40788
==========================================
- Hits 141705 141703 -2
- Misses 31325 31327 +2
Partials 2662 2662
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| # legacy Hive spec — whose `HiveEngineSpec.execute()` passes an `async` kwarg that | ||
| # the real `databricks.sql.client.Cursor` rejects (SUPERSET-PYTHON-179V, | ||
| # apache/superset#24786). | ||
| class DatabricksPythonConnectorEngineSpec(DatabricksDynamicBaseEngineSpec): |
There was a problem hiding this comment.
Suggestion: Unrecognized legacy URIs now select this spec, whose parser requires http_path, catalog, and schema; missing keys raise KeyError, causing database parameters to become empty.
Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Api mismatch
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset/db_engine_specs/databricks.py
**Line:** 596:600
**Comment:**
*Api Mismatch: Unrecognized legacy URIs now select this spec, whose parser requires `http_path`, `catalog`, and `schema`; missing keys raise `KeyError`, causing database parameters to become empty.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
The flagged issue is correct. When an unrecognized legacy URI selects a spec that expects specific parameters ( To resolve this, you should implement a validation check or provide default values within the Example fix for def validate_parameters(properties: dict[str, Any], ...) -> list[dict[str, Any]]:
# Ensure required keys exist to prevent KeyError
required = {"http_path", "catalog", "schema"}
for key in required:
if key not in properties:
properties[key] = "" # Or appropriate default
# ... existing validation logicWould you like me to fetch all other comments on this PR to validate and implement fixes for them as well? superset/db_engine_specs/databricks.py |
SUMMARY
Fixes
TypeError: Cursor.execute() got an unexpected keyword argument 'async'raised from
DatabaseRestApi.table_extra_metadatafor certain Databricksconnections. Reproduces on every table-metadata lookup (SQL Lab / dataset
panel) for any affected connection.
Root cause.
get_engine_spec(backend, driver)matches a driver stringexactly first; if no registered spec's
driversdict matches, it falls back tothe first backend-matching spec in module-definition order. For
engine="databricks"there are four concrete specs.DatabricksHiveEngineSpecwas defined first, so any stored Databricks
sqlalchemy_uriwhose driver suffixdoesn't exactly match one of the four registered driver keys (e.g. a URI saved
before the
databricks-sql-pythonkey existed, or a hand-written/legacy suffix)fell through to this ambiguous fallback and was resolved as
DatabricksHiveEngineSpec— even though the URL still resolves to and actuallyconnects via the
databricks-sql-connectorpackage'sdatabricks.sql.client.Cursor.DatabricksHiveEngineSpecinheritsHiveEngineSpec.execute(), whichunconditionally calls
cursor.execute(query, **{"async": async_}). A realpyhivecursor swallows theasynckwarg, butdatabricks.sql.client.Cursorhas a fixed signature with no
**kwargs, so it raises theTypeError. This isreached from
PrestoEngineSpec.get_create_view(inherited by Hive) on everyget_extra_table_metadatacall.Call chain:
databases/api.py:table_extra_metadata->presto.py:get_extra_table_metadata->
presto.py:get_create_view->hive.py:execute-> databricks cursor.Precedent. This is the same crash as #24786 (2024, "Hive
asynckwarg unexpected keyword when connecting to Databricks Unity Catalog"),via a different call path (SQL Lab query execution). The naive fix there
(PR #24984 — remove the
asynckwarg fromHiveEngineSpec.execute) was closedunmerged because it would break
async_/query-cancellation semantics for genuinepyhive/Presto connections. There is also an explicit test,
test_presto.py::test_get_create_view_propagates_other_errors, asserting thatnon-
DatabaseErrorexceptions propagate out ofget_create_view, so wideningthat
exceptclause is intentionally off-limits too.The fix. Rather than touch the shared Hive/Presto code paths, this reorders
the concrete Databricks engine-spec class definitions so the general-purpose
DatabricksPythonConnectorEngineSpecis defined first and therefore wins theambiguous-driver fallback, with the legacy Native/ODBC/Hive specs defined after
it.
load_engine_specs()iteratesmodule.__dict__, which preservesdefinition order, so the fallback now lands on the modern connector — which uses
databricks.sql.client.Cursordirectly and never goes throughHiveEngineSpec.execute(). Pure relocation of class definitions plus commentsdocumenting the ordering invariant; no logic changes.
TESTING INSTRUCTIONS
test_databricks.py::test_get_engine_spec_unrecognized_driver_prefers_python_connectorasserts
get_engine_spec("databricks", "some-unrecognized-driver-name")nowreturns
DatabricksPythonConnectorEngineSpec, and that exact matches stillresolve correctly (
pyhive-> Hive,databricks-sql-python-> Python connector).pytest tests/unit_tests/db_engine_specs/test_databricks.py tests/unit_tests/db_engine_specs/test_presto.py— 200 passed.pytest tests/unit_tests/db_engine_specs/test_init.py— 6 passed.pytest tests/integration_tests/db_engine_specs/databricks_tests.py::...::test_get_engine_spec— passed.pre-commit run --files ...— ruff, ruff-format, mypy, pylint, db-engine-spec metadata validation all pass.TRADEOFFS
stored URI whose driver suffix matches no registered spec exactly) will now be
resolved as
DatabricksPythonConnectorEngineSpecinstead ofDatabricksHiveEngineSpec.table_extra_metadata/get_create_viewpath this turns a hard crashinto a working call.
change as well, but this is a strict improvement: the old Hive fallback was
already broken for anything depending on a real Hive cursor (that's the crash),
and the new fallback matches the connector that URI is actually
resolvable/connectable with (
databricks-sql-connector).+pyhive/+pyodbc/+connector/+databricks-sql-pythonURIs are completely unaffected — exact driver match still wins before the
fallback is ever consulted.
get_engine_spec("databricks", None)(a baredatabricks://URI, exactly what the bug: Hiveasynckwarg unexpected keyword when connecting to Databricks Unity Catalog #24786 reporters used) is also affected:it skips the exact-match loop and hits the same fallback, so it now resolves to
DatabricksPythonConnectorEngineSpecinstead ofDatabricksHiveEngineSpec.Same strict improvement — that URI connects via the Python connector.
shared
get_engine_specmechanism (any backend with multiple specs and anunrecognized driver has the same "random first-defined spec" outcome). This PR
only fixes it for Databricks via file ordering and deliberately does not
change the shared mechanism (bigger blast radius). A general fix (e.g. an
explicit fallback/priority marker) is a possible follow-up.
ADDITIONAL INFORMATION
asynckwarg unexpected keyword when connecting to Databricks Unity Catalog #24786