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]

Reply via email to