This is an automated email from the ASF dual-hosted git repository.
aminghadersohi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 16271bbfbb7 fix(databricks): stop the string-type patch writing
SQLAlchemy's shared colspecs (#44707)
16271bbfbb7 is described below
commit 16271bbfbb7f003ea568c0ab3327dc48023365ca
Author: Amin Ghadersohi <[email protected]>
AuthorDate: Wed Sep 30 15:17:47 2026 +1000
fix(databricks): stop the string-type patch writing SQLAlchemy's shared
colspecs (#44707)
---
superset/db_engine_specs/databricks.py | 28 ++++++---
.../unit_tests/db_engine_specs/test_databricks.py | 70 ++++++++++++++++++++++
2 files changed, 90 insertions(+), 8 deletions(-)
diff --git a/superset/db_engine_specs/databricks.py
b/superset/db_engine_specs/databricks.py
index 12f0c772e73..b572832fb3b 100644
--- a/superset/db_engine_specs/databricks.py
+++ b/superset/db_engine_specs/databricks.py
@@ -96,13 +96,18 @@ def monkeypatch_dialect() -> None:
"""
Monkeypatch dialect to correctly escape single quotes for Databricks.
- The Databricks SQLAlchemy dialect (<3.0) incorrectly escapes single quotes
by
- doubling them ('O''Hara') instead of using backslash escaping ('O\'Hara').
The
- fixed version requires SQLAlchemy>=2.0, which is not yet compatible with
Superset.
-
- Since the DatabricksDialect.colspecs points to the base class
(HiveDialect.colspecs)
- we can't patch it without affecting other Hive-based dialects. The
solution is to
- introduce a dialect-aware string type so that the change applies only to
Databricks.
+ This compatibility patch covers HiveDialect-based Databricks dialects from
+ sqlalchemy-databricks. The supported databricks-sqlalchemy dialect defines
its
+ own colspecs and escaping; it is not a target of this patch.
+
+ A dialect-aware string type preserves ordinary Hive literal handling while
+ applying backslash escaping to Hive-based Databricks dialects.
+
+ PyHive's HiveDialect does not define ``colspecs``, so
``HiveDialect.colspecs`` is
+ SQLAlchemy's ``DefaultDialect.colspecs`` dict, shared by every dialect
that does
+ not define its own. The patch therefore gives HiveDialect its own copy
instead of
+ writing to the shared dict, which would change the string types of
unrelated
+ dialects (and make ``sa.Enum`` fail to adapt on them).
"""
try:
from pyhive.sqlalchemy_hive import HiveDialect
@@ -118,7 +123,14 @@ def monkeypatch_dialect() -> None:
return DatabricksStringType().literal_processor(dialect)
return super().literal_processor(dialect)
- HiveDialect.colspecs[types.String] = ContextAwareStringType
+ # Copy, never write to the shared parent dict. Enum is a String
subclass;
+ # map it to itself so it is not adapted to the decorator, which cannot
+ # take Enum's arguments. Enum literals retain their own escaping.
+ HiveDialect.colspecs = {
+ **HiveDialect.colspecs,
+ types.String: ContextAwareStringType,
+ types.Enum: types.Enum,
+ }
except ImportError:
pass
diff --git a/tests/unit_tests/db_engine_specs/test_databricks.py
b/tests/unit_tests/db_engine_specs/test_databricks.py
index fe147df0928..cd1ac465d8b 100644
--- a/tests/unit_tests/db_engine_specs/test_databricks.py
+++ b/tests/unit_tests/db_engine_specs/test_databricks.py
@@ -1198,3 +1198,73 @@ def
test_get_engine_spec_unrecognized_driver_prefers_python_connector() -> None:
get_engine_spec("databricks", "databricks-sql-python")
is DatabricksPythonConnectorEngineSpec
)
+
+
+def test_monkeypatch_dialect_leaves_shared_colspecs_alone(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ """
+ The Databricks string patch must not reach dialects outside the Hive
family.
+
+ PyHive's HiveDialect inherits SQLAlchemy's shared
``DefaultDialect.colspecs``;
+ writing to it changed String/Unicode/Text/Enum handling for every dialect
that
+ does not define its own ``colspecs``.
+ """
+ import enum
+
+ import sqlalchemy as sa
+ from sqlalchemy.engine.default import DefaultDialect
+
+ pyhive = pytest.importorskip("pyhive.sqlalchemy_hive")
+
+ from superset.db_engine_specs.databricks import monkeypatch_dialect
+
+ class Color(enum.Enum):
+ red = "red"
+
+ class OtherDialect(DefaultDialect):
+ """A dialect that relies on the shared colspecs."""
+
+ monkeypatch.setattr(pyhive.HiveDialect, "colspecs",
pyhive.HiveDialect.colspecs)
+ monkeypatch_dialect() # also ran when the module was imported
+
+ assert sa.String not in DefaultDialect.colspecs
+ other = OtherDialect()
+ assert type(sa.Unicode(5).dialect_impl(other)) is sa.Unicode
+ assert type(sa.Text().dialect_impl(other)) is sa.Text
+
+ hive = pyhive.HiveDialect
+ assert hive.colspecs is not DefaultDialect.colspecs
+ assert hive.colspecs[sa.String].__name__ == "ContextAwareStringType"
+ for dialect in (other, hive()):
+ impl = sa.Enum(Color).dialect_impl(dialect)
+ assert impl.result_processor(dialect, None)("red") is Color.red
+
+
+def test_monkeypatch_dialect_preserves_hive_databricks_escaping(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ """The isolated mapping still escapes Hive-based Databricks literals."""
+ import sqlalchemy as sa
+
+ pyhive = pytest.importorskip("pyhive.sqlalchemy_hive")
+ pytest.importorskip("databricks.sql")
+
+ from pyhive.sqlalchemy_hive import HiveDialect
+
+ from superset.db_engine_specs.databricks import monkeypatch_dialect
+
+ class DatabricksDialect(HiveDialect):
+ """Represent the legacy Hive-based Databricks dialect."""
+
+ monkeypatch.setattr(pyhive.HiveDialect, "colspecs",
pyhive.HiveDialect.colspecs)
+ monkeypatch_dialect()
+
+ assert (
+ str(
+ sa.literal("O'Hara").compile(
+ dialect=DatabricksDialect(), compile_kwargs={"literal_binds":
True}
+ )
+ )
+ == r"'O\'Hara'"
+ )