You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by rb...@apache.org on 2026/10/07 17:19:22 UTC
(superset) branch master updated: fix(semantic): reject SQL clauses on semantic views (#44899)
This is an automated email from the ASF dual-hosted git repository.
rebenitez1802 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 fca6bdad57b fix(semantic): reject SQL clauses on semantic views (#44899)
fca6bdad57b is described below
commit fca6bdad57bc25361576dc9c49d5e9081bed1803
Author: Mike Bridge <mi...@preset.io>
AuthorDate: Wed Oct 7 11:19:14 2026 -0600
fix(semantic): reject SQL clauses on semantic views (#44899)
---
.../using-superset/semantic-member-selections.mdx | 4 ++++
superset/common/query_object.py | 15 +++++++++++++++
superset/semantic_layers/mapper.py | 14 ++++++++++----
superset/translations/messages.pot | 3 +++
tests/unit_tests/queries/query_object_test.py | 22 ++++++++++++++++++++--
5 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/docs/docs/using-superset/semantic-member-selections.mdx b/docs/docs/using-superset/semantic-member-selections.mdx
index b57fc56688a..e3e5a75288e 100644
--- a/docs/docs/using-superset/semantic-member-selections.mdx
+++ b/docs/docs/using-superset/semantic-member-selections.mdx
@@ -56,6 +56,10 @@ MCP `list_metrics`). The server never adds a missing marker from metadata.
Explore query controls remain unavailable until explicit field initialization,
so legacy filter subjects cannot trigger value suggestions before reselection.
+Custom SQL `WHERE` and `HAVING` clauses are not supported on semantic views.
+Remove those clauses or use semantic dimension filters instead. Chart-data API
+requests with these clauses return HTTP 400 before querying the provider.
+
For versioned semantic views, Matrixify **All** mode with **A to Z** or **Z to A**
sorting cannot populate or refresh dimension values from suggestions. Existing
saved values are preserved. For a new selection, switch to **Members** and enter
diff --git a/superset/common/query_object.py b/superset/common/query_object.py
index 51e4f0290bb..bd354205910 100644
--- a/superset/common/query_object.py
+++ b/superset/common/query_object.py
@@ -411,6 +411,7 @@ class QueryObject: # pylint: disable=too-many-instance-attributes
) -> QueryObjectValidationError | None:
"""Validate query object"""
try:
+ self._validate_semantic_view_extras()
if self.datasource and self.datasource.type == "semantic_view":
implementation: SemanticViewABC = cast(
"SemanticView", self.datasource
@@ -454,6 +455,20 @@ class QueryObject: # pylint: disable=too-many-instance-attributes
raise
return ex
+ def _validate_semantic_view_extras(self) -> None:
+ """Reject unsupported SQL clauses before resolving a semantic provider."""
+ if (
+ self.datasource
+ and self.datasource.type == "semantic_view"
+ and (self.extras.get("where") or self.extras.get("having"))
+ ):
+ raise QueryObjectValidationError(
+ _(
+ "SQL WHERE/HAVING expressions are not supported for semantic "
+ "views. Remove the SQL clause or use a semantic dimension filter."
+ )
+ )
+
def _validate_no_have_duplicate_labels(self) -> None:
all_labels = self.metric_names + self.column_names
if len(set(all_labels)) < len(all_labels):
diff --git a/superset/semantic_layers/mapper.py b/superset/semantic_layers/mapper.py
index 5e7973f738d..303e73f17da 100644
--- a/superset/semantic_layers/mapper.py
+++ b/superset/semantic_layers/mapper.py
@@ -499,11 +499,17 @@ def _get_filters_from_query_object(
def _get_filters_from_extras(extras: dict[str, Any]) -> set[Filter]:
"""
- Extract filters from the extras dict.
+ Convert SQL extras into ADHOC filters for direct mapper callers.
+
+ The chart-data path rejects non-empty SQL WHERE/HAVING extras for semantic
+ views before reaching this mapper. This conversion is therefore unreachable
+ through that validated path; it does not establish provider support for
+ arbitrary SQL predicates. Exposing it there would require a provider-specific
+ sanitization contract.
The extras dict can contain various keys that affect query behavior:
- Supported keys (converted to filters):
+ Direct mapper inputs (converted to filters, not chart-data support):
- "where": SQL WHERE clause expression (e.g., "customer_id > 100")
- "having": SQL HAVING clause expression (e.g., "SUM(sales) > 1000")
@@ -511,8 +517,8 @@ def _get_filters_from_extras(extras: dict[str, Any]) -> set[Filter]:
- "time_grain_sqla": Time granularity (e.g., "P1D", "PT1H")
Handled in _convert_time_grain() and used for dimension grain matching
- Note: The WHERE and HAVING clauses from extras are SQL expressions that
- are passed through as-is to the semantic layer as adhoc Filter objects.
+ For direct mapper calls, WHERE and HAVING text is passed unchanged into
+ ADHOC Filter objects; this helper does not sanitize it.
"""
filters: set[Filter] = set()
diff --git a/superset/translations/messages.pot b/superset/translations/messages.pot
index d9e722be8c6..a0711b09d89 100644
--- a/superset/translations/messages.pot
+++ b/superset/translations/messages.pot
@@ -11256,6 +11256,9 @@ msgstr ""
msgid "SQL Query"
msgstr ""
+msgid "SQL WHERE/HAVING expressions are not supported for semantic views. Remove the SQL clause or use a semantic dimension filter."
+msgstr ""
+
msgid "SQL expression"
msgstr ""
diff --git a/tests/unit_tests/queries/query_object_test.py b/tests/unit_tests/queries/query_object_test.py
index c1d4ab70616..616658f49b8 100644
--- a/tests/unit_tests/queries/query_object_test.py
+++ b/tests/unit_tests/queries/query_object_test.py
@@ -16,7 +16,7 @@
# under the License.
from contextlib import contextmanager
from datetime import datetime
-from unittest.mock import call, patch
+from unittest.mock import call, patch, PropertyMock
import pandas as pd
import pytest
@@ -25,8 +25,9 @@ from pandas import DataFrame
from superset.common.query_object import QueryObject
from superset.connectors.sqla.models import SqlaTable
-from superset.exceptions import InvalidPostProcessingError
+from superset.exceptions import InvalidPostProcessingError, QueryObjectValidationError
from superset.models.core import Database
+from superset.semantic_layers.models import SemanticView
from superset.superset_typing import Metric
from superset.utils import pandas_postprocessing
from superset.utils.core import override_user
@@ -90,6 +91,23 @@ def test_default_query_object_to_dict():
}
+@pytest.mark.parametrize("clause", ["where", "having"])
+def test_semantic_view_rejects_custom_sql_extras(clause: str) -> None:
+ """Unsupported SQL clauses fail before semantic provider access."""
+ view: SemanticView = SemanticView(name="Orders")
+ provider_access: PropertyMock = PropertyMock(
+ side_effect=AssertionError("provider accessed before SQL extras validation")
+ )
+ query: QueryObject = QueryObject(datasource=view, extras={clause: "amount > 10"})
+
+ with patch.object(SemanticView, "implementation", provider_access):
+ with pytest.raises(
+ QueryObjectValidationError, match="SQL WHERE/HAVING.*semantic views"
+ ):
+ query.validate()
+ provider_access.assert_not_called()
+
+
def test_exec_post_processing_rejects_unsupported_operation():
"""
An unknown operation reports itself in the error message rather than failing