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'"
+    )

Reply via email to