mikebridge opened a new pull request, #43777:
URL: https://github.com/apache/superset/pull/43777
### SUMMARY
`GET /api/v1/datasource/semantic_view/<id>/column/<col>/values/` has
returned HTTP 500 on every semantic view since semantic layers shipped
(#37815): the datasource values endpoint duck-types an `ExploreMixin`
datasource and reads `datasource.normalize_columns`, then calls
`datasource.values_for_column(...)`, and `SemanticView` defined neither. The
Explore adhoc-filter popover swallows the failure into an empty suggestion
list, so the defect was invisible outside the server log and the browser
Network tab.
```
File "/app/superset/datasource/api.py", line 135, in get_column_values
denormalize_column = not datasource.normalize_columns
AttributeError: 'SemanticView' object has no attribute 'normalize_columns'
```
This PR implements both members on `SemanticView` and leaves the endpoint
untouched, so datasets are byte-for-byte unaffected and the endpoint's caching,
RLS cache-key fingerprint, `changed_on` busting and `X-Cache-Status` contract
all apply to semantic views unchanged.
- `normalize_columns` → `False` (dimension names are provider-verbatim).
- `values_for_column(column_name, limit, denormalize_column, array_elements,
search)` delegates to the provider ABC's purpose-built abstract
`get_values(dimension, filters)` — its first consumer. The dimension is
resolved from the same grain-collapsed set the picker sees, so an unknown name
(a metric name included) raises `KeyError`, which the endpoint reports as the
400 naming the column, exactly as datasets do.
- Search text (`q`, added to the endpoint by #43518) narrows at the provider
with a containment `LIKE` filter, so values beyond the first page are findable.
A provider that rejects the filter (a non-text dimension, say) degrades to the
unfiltered bounded page with a logged warning, never to an error.
- `get_values` takes no limit or order, so the host sorts ascending (nulls
first) and then truncates to the endpoint's limit — sort-before-truncate so the
page is deterministic rather than an engine-arbitrary subset.
- Extension-typed columns are stringified before `to_pylist()` (plain
`to_pylist` yields `uuid.UUID` objects), so the payload is JSON-serialisable.
- `denormalize_column` / `array_elements` are dataset concepts with no
semantic-view counterpart; accepted for signature compatibility and ignored.
Two documented parity gaps with datasets, both inherent to the standard
filter model: case sensitivity follows the provider's collation (no
case-folding operator), and `%`/`_` in the search term act as wildcards (the
`Filter` model has no portable escape declaration; over-matching is the safe
failure for suggestions).
Not addressed here, tracked separately: the popover should surface a
server-side failure as "suggestions unavailable" instead of a silent empty list
— that is what let this 500 go unreported.
### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
**Before:** opening the value field of an adhoc filter on any semantic-view
dimension fires the values request, which 500s with the traceback above; the
picker shows an empty list.
**After** (Explore on Snowflake's TPC-DS `TPCDS_SEMANTIC_VIEW_SM` via the
Snowflake semantic-layer extension, dimension `CATEGORY`): the picker lists the
11 distinct values, `<NULL>` first, then `Books`, `Children`, `Electronics`,
`Home`, `Jewelry`, `Men`, `Music`, …
### TESTING INSTRUCTIONS
Automated:
```bash
pytest tests/unit_tests/semantic_layers/ -q # 416 passed; 13 new (10
model + 3 endpoint-level through the Flask client)
```
Manual, on any instance with `SEMANTIC_LAYERS` enabled and a semantic view
added (the demo/pandas layer suffices):
1. Explore the view → Filters → **+** → pick any dimension → click into the
value field. The picker populates; choosing a value applies the filter. In
devtools → Network, `/column/<dim>/values/` is `200` with `X-Cache-Status:
MISS`.
2. Open the same picker again: the response is `X-Cache-Status: HIT` with
the same payload. `?force=true` (or editing the view) queries the provider
again.
3. Type into the value field on a dimension with more values than the limit:
a term matching an out-of-page value (matching case) returns it — narrowing
happens at the provider (`?q=`).
4. `curl` the endpoint with a column name that is not a dimension of the
view (a metric name works): `400 "Column name X does not exist"`, same as a
dataset.
5. Open the same picker on a regular dataset: unchanged from master.
Verified against a real provider (Snowflake, TPC-DS sample semantic view):
fresh fetch `200 MISS` with 11 values nulls-first; repeat `HIT`; `q=en` →
`[Children, Men, Women]`, `q=oo` → `[Books]`; unknown column → 400.
### ADDITIONAL INFORMATION
<!--- Check any relevant boxes with "x" -->
<!--- HINT: Include "Fixes #nnn" if you are fixing an existing issue -->
- [ ] Has associated issue:
- [x] Required feature flags: `SEMANTIC_LAYERS`
- [ ] Changes UI
- [ ] Includes DB Migration (follow approval process in
[SIP-59](https://github.com/apache/superset/issues/13351))
- [ ] Migration is atomic, supports rollback & is backwards-compatible
- [ ] Confirm DB migration upgrade and downgrade tested
- [ ] Runtime estimates and downtime expectations provided
- [ ] Introduces new feature or API
- [ ] Removes existing feature or API
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]