You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/10/22 00:12:56 UTC
(superset) branch master updated: fix(semantic-layer): handle absent fetch-values predicate (#45539)
This is an automated email from the ASF dual-hosted git repository.
rusackas 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 7ff7f9048bf fix(semantic-layer): handle absent fetch-values predicate (#45539)
7ff7f9048bf is described below
commit 7ff7f9048bfdda992acb4f815d2d60ae00e0ab71
Author: Mike Bridge <mi...@preset.io>
AuthorDate: Wed Oct 21 18:12:49 2026 -0600
fix(semantic-layer): handle absent fetch-values predicate (#45539)
Co-authored-by: Mike Bridge <mi...@ext.preset.io>
---
docs/developer_docs/extensions/semantic-filters.md | 4 ++
superset/semantic_layers/models.py | 3 +
.../semantic_layers/metric_filter_test.py | 66 ++++++++++++++++++++++
3 files changed, 73 insertions(+)
diff --git a/docs/developer_docs/extensions/semantic-filters.md b/docs/developer_docs/extensions/semantic-filters.md
index 907f9bf7cf0..a49f0235ae8 100644
--- a/docs/developer_docs/extensions/semantic-filters.md
+++ b/docs/developer_docs/extensions/semantic-filters.md
@@ -39,6 +39,10 @@ and variable-length tuples. SDK `FilterValues` also includes `None`.
## Provider compatibility
+Semantic views do not define a dataset-level SQL fetch-values predicate. Setting
+`apply_fetch_values_predicate: true` on a chart-data query is therefore a no-op for
+semantic views; explicit query filters still apply, including group-limit filters.
+
`OrFilter` is mandatory core vocabulary, not an optional capability. Every
provider must render groups for main queries, row counts and group-limit
subqueries. Upgrade adapters before, or atomically with, the host/SDK upgrade;
diff --git a/superset/semantic_layers/models.py b/superset/semantic_layers/models.py
index 3675c44bf9c..9e946f7edcd 100644
--- a/superset/semantic_layers/models.py
+++ b/superset/semantic_layers/models.py
@@ -340,6 +340,9 @@ class SemanticView(AuditMixinNullable, Model):
supports_samples: bool = False
supports_drill_to_detail: bool = False
+ # Semantic views have no dataset-level SQL predicate for fetching filter values.
+ fetch_values_predicate: None = None
+
# Use integer as the primary key for cross-database auto-increment
# compatibility (sa.Identity() is not supported in MySQL or SQLite).
# The uuid column is a secondary unique identifier used in URLs and perms.
diff --git a/tests/unit_tests/semantic_layers/metric_filter_test.py b/tests/unit_tests/semantic_layers/metric_filter_test.py
index 7760849d37a..b9ef50978ae 100644
--- a/tests/unit_tests/semantic_layers/metric_filter_test.py
+++ b/tests/unit_tests/semantic_layers/metric_filter_test.py
@@ -18,6 +18,7 @@
"""Non-dimension filters fail instead of returning silently unfiltered data."""
from collections.abc import Callable
+from datetime import datetime
from typing import Any
from unittest.mock import MagicMock, PropertyMock
@@ -35,6 +36,7 @@ from superset_core.semantic_layers.types import (
SemanticQuery,
SemanticResult,
)
+from superset_core.semantic_layers.view import SemanticViewFeature
from werkzeug.test import TestResponse
from superset.charts.data.dashboard_filter_context import apply_dashboard_filter_context
@@ -45,6 +47,7 @@ from superset.mcp_service.chart.chart_helpers import (
merge_extra_form_data_filters_into_query,
)
from superset.semantic_layers.mapper import (
+ _get_group_limit_filters,
map_query_object,
validate_query_object,
ValidatedQueryObject,
@@ -112,6 +115,69 @@ def _request(filters: list[QueryObjectFilterClause]) -> dict[str, Any]:
}
+@pytest.mark.parametrize("group_limit", [False, True])
+def test_chart_data_fetch_values_predicate_without_configured_predicate(
+ client: FlaskClient,
+ full_api_access: None,
+ metric_view: tuple[SemanticView, MagicMock],
+ group_limit: bool,
+) -> None:
+ """An absent optional predicate must not fail or discard explicit filters."""
+ payload: dict[str, Any] = _request(
+ [{"col": "category", "op": "==", "val": "Books"}]
+ )
+ payload["queries"][0]["apply_fetch_values_predicate"] = True
+ if group_limit:
+ metric_view[1].features = frozenset({SemanticViewFeature.GROUP_LIMIT})
+ payload["queries"][0].update(
+ series_limit=1,
+ series_columns=["category"],
+ )
+
+ response: TestResponse = client.post("/api/v1/chart/data", json=payload)
+
+ assert response.status_code == 200, response.json
+ assert response.json is not None
+ assert response.json["result"][0]["data"] == [
+ {"category": "Books", "total_amount": 7}
+ ]
+ query: SemanticQuery = metric_view[1].get_table.call_args.args[0]
+ expected_filters: set[Filter] = {
+ Filter(
+ type=PredicateType.WHERE,
+ column=next(iter(metric_view[1].get_dimensions())),
+ operator=Operator.EQUALS,
+ value="Books",
+ )
+ }
+ assert query.filters == expected_filters
+ if group_limit:
+ assert query.group_limit is not None
+ assert query.group_limit.filters is None # Inherit the main query filters.
+
+
+def test_group_limit_fetch_values_predicate_without_configured_predicate(
+ metric_view: tuple[SemanticView, MagicMock],
+) -> None:
+ """Separate inner bounds also tolerate the absent optional predicate."""
+ dimension: Dimension = next(iter(metric_view[1].get_dimensions()))
+ query: ValidatedQueryObject = ValidatedQueryObject(
+ datasource=metric_view[0],
+ apply_fetch_values_predicate=True,
+ inner_from_dttm=datetime(2025, 10, 1),
+ inner_to_dttm=datetime(2025, 10, 2),
+ filters=[{"col": "category", "op": "==", "val": "Books"}],
+ )
+ assert _get_group_limit_filters(query, {dimension.name: dimension}) == {
+ Filter(
+ type=PredicateType.WHERE,
+ column=dimension,
+ operator=Operator.EQUALS,
+ value="Books",
+ )
+ }
+
+
@pytest.mark.parametrize(("column", "error"), REFUSED_CHART_FILTERS)
@pytest.mark.parametrize("mixed", [False, True])
def test_chart_data_rejects_non_dimension_filters(