This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch fix/issue-41795-open-pr
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 42fd8e19fa7e65657a0fb93ae69411beff4f9641
Author: Evan <[email protected]>
AuthorDate: Mon Jul 6 11:41:14 2026 -0700

    fix(sqla): cast native UUID columns to string for LIKE/ILIKE filters
    
    Table chart server-pagination search builds an ILIKE filter against the
    searched column. Native UUID columns (PostgreSQL, ClickHouse) map to
    GenericDataType.STRING, so the existing not-a-string guard skipped the
    cast and the raw ILIKE hit the UUID column type, which the database
    rejects (e.g. ClickHouse: "Illegal type UUID of argument of function
    ilike"). Detect UUID native types and force the string cast so
    searching UUID columns works.
    
    Fixes #41795
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 superset/models/helpers.py              | 24 +++++++++++-
 tests/unit_tests/models/helpers_test.py | 68 +++++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index 9ca87780be9..05231369336 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -250,6 +250,18 @@ def json_to_dict(json_str: str) -> dict[Any, Any]:
     return {}
 
 
+def is_uuid_native_type(native_type: Optional[str]) -> bool:
+    """
+    Return True if a native column type represents a UUID.
+
+    Engines such as PostgreSQL and ClickHouse expose native UUID column types
+    (e.g. ``UUID``, ``Nullable(UUID)``) that map to ``GenericDataType.STRING``
+    yet reject LIKE/ILIKE against the raw column, so these columns need an
+    explicit cast to string before pattern matching.
+    """
+    return native_type is not None and "uuid" in native_type.lower()
+
+
 def convert_uuids(obj: Any) -> Any:
     """
     Convert UUID objects to str so we can use yaml.safe_dump
@@ -3746,7 +3758,11 @@ class ExploreMixin:  # pylint: 
disable=too-many-public-methods
                         utils.FilterOperator.ILIKE,
                         utils.FilterOperator.LIKE,
                     }:
-                        if target_generic_type != GenericDataType.STRING:
+                        # Native UUID columns report GenericDataType.STRING but
+                        # reject LIKE/ILIKE without a cast (see issue #41795)
+                        if target_generic_type != GenericDataType.STRING or (
+                            is_uuid_native_type(col_type)
+                        ):
                             sqla_col = sa.cast(sqla_col, sa.String)
 
                         if op == utils.FilterOperator.LIKE:
@@ -3757,7 +3773,11 @@ class ExploreMixin:  # pylint: 
disable=too-many-public-methods
                         utils.FilterOperator.NOT_LIKE,
                         utils.FilterOperator.NOT_ILIKE,
                     }:
-                        if target_generic_type != GenericDataType.STRING:
+                        # Native UUID columns report GenericDataType.STRING but
+                        # reject LIKE/ILIKE without a cast (see issue #41795)
+                        if target_generic_type != GenericDataType.STRING or (
+                            is_uuid_native_type(col_type)
+                        ):
                             sqla_col = sa.cast(sqla_col, sa.String)
 
                         if op == utils.FilterOperator.NOT_LIKE:
diff --git a/tests/unit_tests/models/helpers_test.py 
b/tests/unit_tests/models/helpers_test.py
index 9603e4cd0f5..4993ddc80a8 100644
--- a/tests/unit_tests/models/helpers_test.py
+++ b/tests/unit_tests/models/helpers_test.py
@@ -3410,3 +3410,71 @@ def test_get_sqla_query_dotted_struct_column_bigquery(
     # ```forecasts.original`.`total_cost``` (the regression), so this negative
     # assertion catches the actual failure mode, not just an exact-string 
match.
     assert "`forecasts.original`" not in sql
+
+
[email protected](
+    "native_type",
+    ["UUID", "uuid", "Nullable(UUID)"],
+)
[email protected](
+    "op",
+    ["LIKE", "ILIKE", "NOT LIKE", "NOT ILIKE"],
+)
+def test_like_filter_on_uuid_column_casts_to_string(
+    database: Database, native_type: str, op: str
+) -> None:
+    """
+    LIKE-family filters on native UUID columns must cast the column to string.
+
+    UUID columns map to ``GenericDataType.STRING``, so the generic-type guard
+    alone skips the string cast — but engines such as PostgreSQL and ClickHouse
+    reject LIKE/ILIKE against a raw UUID column (issue #41795: table chart
+    server-pagination search fails with e.g. "Illegal type UUID of argument of
+    function ilike"). The native column type must force the cast.
+    """
+    from superset.connectors.sqla.models import SqlaTable, TableColumn
+
+    table = SqlaTable(
+        database=database,
+        schema=None,
+        table_name="t",
+        columns=[TableColumn(column_name="event_id", type=native_type)],
+    )
+
+    result = table.get_sqla_query(
+        columns=["event_id"],
+        metrics=[],
+        extras={},
+        filter=[{"col": "event_id", "op": op, "val": "abc%"}],
+        granularity=None,
+        is_timeseries=False,
+        orderby=[],
+    )
+    sql = str(result.sqla_query)
+    assert "CAST" in sql.upper(), f"Expected string cast in SQL: {sql}"
+
+
+def test_like_filter_on_string_column_does_not_cast(database: Database) -> 
None:
+    """
+    LIKE-family filters on plain string columns must not add a redundant cast.
+    """
+    from superset.connectors.sqla.models import SqlaTable, TableColumn
+
+    table = SqlaTable(
+        database=database,
+        schema=None,
+        table_name="t",
+        columns=[TableColumn(column_name="b", type="TEXT")],
+    )
+
+    result = table.get_sqla_query(
+        columns=["b"],
+        metrics=[],
+        extras={},
+        filter=[{"col": "b", "op": "ILIKE", "val": "abc%"}],
+        granularity=None,
+        is_timeseries=False,
+        orderby=[],
+    )
+    sql = str(result.sqla_query)
+    assert "CAST" not in sql.upper(), f"Unexpected cast in SQL: {sql}"

Reply via email to