Skip to content

feat(semantic layers): derive Explore picker modes from declared semantic view features - #42540

Draft
mikebridge wants to merge 4 commits into
apache:masterfrom
mikebridge:sc-107939-cube-picker-options
Draft

feat(semantic layers): derive Explore picker modes from declared semantic view features#42540
mikebridge wants to merge 4 commits into
apache:masterfrom
mikebridge:sc-107939-cube-picker-options

Conversation

@mikebridge

@mikebridge mikebridge commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Semantic-view providers can now declare what their backend supports, and the Explore column picker adapts its modes and dimension classification to that declaration instead of guessing from column metadata.

A note on the originating report. The internal story behind this describes the "Columns and metrics" picker showing 0 item(s) for a Cube-backed semantic view, and that literal symptom no longer reproduces on master. Reproduced against a live Cube instance through the real model path: dimensions arrive with expression=None, so the picker's expression-based split routes all four into Simple, not zero.

The empty picker came from the pre-#41456 payload, which set expression=dimension.definition. Because ColumnSelectPopover treats any column with a truthy expression as a Saved/calculated column, every dimension landed under Saved and Simple rendered 0 item(s). #41456 (e28b259de0) flipped that field to None so temporal dimensions keep their time-grain affordance — and the dimensions moved into Simple.

That fixed the emptiness but landed them in the wrong mode. The behavior agreed with design at the time was: dimensions and metrics presented under Saved, with Simple and Custom SQL disabled, because a semantic view whose backend cannot execute adhoc column expressions should not offer modes that compose them. The "disable Simple" half was never implemented, and #41456 additionally moved dimensions out of Saved. This PR implements that intent — generalized so it is driven by what a provider declares rather than by which provider it is:

1. Providers declare features; Explore consumes capabilities. A new opt-in SemanticViewFeature.ADHOC_COLUMN_EXPRESSIONS marks views whose backend accepts simple/custom-SQL column expressions. The declared set serializes to the datasource payload as semantic_view_features. A view that does not declare it gets a Saved-only picker: dimensions listed as Saved options, Simple and Custom SQL visible but disabled, so users cannot compose an expression the backend would reject.

Polarity is opt-in to match the existing provider convention — Snowflake declares its features, Cube and MetricFlow deliberately ship frozenset(). A companion one-line declaration on SnowflakeSemanticView keeps Snowflake's picker behavior unchanged and ships separately in the shell repo.

Provider identity is deliberately not sent over the wire. The @semantic_layer decorator prefixes extension IDs (extensions.preset-io.cube-semantic-layer.cube), so there is no stable bare registry key to publish, and behavior keyed off provider identity would not survive that prefixing. Features are translated exactly once, in utils/pickerCapabilities.ts, into a provider-neutral ColumnPickerCapabilities. Picker components consume capabilities and never read feature strings, registry keys, or display names. Two DnD wrappers that each carried their own copy of the "semantic views disable Custom SQL" rule now defer to that adapter.

2. Compatibility request state was conflated (a real defect). fetchCompatibility dispatched an identical {compatibleMetrics: null, compatibleDimensions: null, compatibilityLoading: false} both when the datasource is non-semantic and when the request failed. A failed compatibility lookup was therefore indistinguishable from "no filtering applies", so no UI could ever surface it. That state is now a discriminated CompatibilityResult (idle | loading | verified | failed) read through typed selectors, and every existing consumer — column picker, datasource-panel drag options, adhoc metric popover — was migrated in the same change so the replacement cannot strand an old reader. Idle, loading, and failed keep the existing no-filter fallback; a verified empty result is a valid "nothing is compatible" answer, not a fallback; failed additionally shows an accessible non-blocking warning where the capability enables it.

Incompatible or legacy adhoc values can no longer be silently committed: Saved becomes the active mode, the legacy value stays inspectable under Custom SQL, and Save is disabled with accessible corrective feedback associated to it until the user explicitly picks a compatible dimension.

expression=None on semantic dimensions is preserved throughout — #41456's time-grain behavior is a protected invariant here, not a cleanup opportunity, and there is a regression assertion to that effect.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Not included: rendering a Cube-backed semantic view requires the proprietary provider extension, which is not installable in an OSS checkout. Behavior is covered by component tests that drive the real picker, and by a local reproduction against a Cube instance described below.

TESTING INSTRUCTIONS

Automated (no provider extension needed):

# Frontend — picker, wrappers, control entry points, state, selectors, adapter
cd superset-frontend
npm test -- src/explore/components/controls/DndColumnSelectControl \
            src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent \
            src/explore/components/controls/MetricControl/AdhocMetricEditPopover \
            src/explore/components/DatasourcePanel \
            src/explore/actions src/explore/reducers src/explore/selectors

# Backend — feature serialization and the #41456 expression/time-grain invariant
pytest tests/unit_tests/semantic_layers/models_test.py

Manual, with a semantic-layer provider available:

  1. Open Explore on a semantic view whose provider declares no features. Open an X-axis or Dimensions control: the picker opens on Saved listing every dimension by verbose name; Simple and Custom SQL are visible but disabled. Select one and save — it commits to the control.
  2. Select a metric first so compatibility narrows, then reopen: dimensions outside the compatible set are visible but disabled. With no compatible dimensions, the picker says so rather than looking like a loading or failed state.
  3. Repeat on a view whose provider declares ADHOC_COLUMN_EXPRESSIONS (and on a plain table dataset): modes, defaults, search, and Save are unchanged.
  4. Repeat through Filter subject and Sort by.

The 0 item(s) claim above was verified against the Cube fixture in db-infra/databases/cube (REST API on :4000) using a throwaway provider stub over SemanticView.data; the stub is not part of this PR.

ADDITIONAL INFORMATION

  • Has associated issue: SC-107939 (Preset-internal)
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • 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

Backwards compatible: semantic_view_features is additive and optional. Payloads without it, unknown feature strings, and non-semantic datasources all resolve to existing behavior, so no UPDATING.md entry is needed.

🤖 Generated with Claude Code

Mike Bridge and others added 4 commits July 28, 2026 16:16
… capability contracts

Serialize SemanticView.features as semantic_view_features in the explorable
datasource payload (new opt-in SemanticViewFeature.ADHOC_COLUMN_EXPRESSIONS),
translate it once through a provider-neutral ColumnPickerCapabilities adapter,
and replace the overlapping compatibleMetrics/compatibleDimensions/
compatibilityLoading Redux fields with a discriminated CompatibilityResult
(idle | loading | verified | failed) consumed through typed selectors.

No provider identity crosses the wire; picker components consume capabilities
only. Verified empty compatibility results are distinguishable from loading
and failure; idle/loading/failed keep the existing no-filter fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… modes for semantic views

Semantic views that do not declare ADHOC_COLUMN_EXPRESSIONS open the column
picker on Saved with every expression-less dimension listed as a Saved option
(verbose-name searchable, metadata unmutated), disable the Simple and Custom
SQL modes, and honor verified compatibility (absent dimensions disabled, a
verified empty list distinct from loading/failure). A failed compatibility
request shows an accessible non-blocking warning and falls back to unfiltered
options. Incompatible or legacy adhoc edited values cannot be saved: Saved
activates, the value stays inspectable, and accessible corrective feedback is
associated with the disabled Save button until a compatible dimension is
explicitly chosen.

Default tab routing is disabled-aware, the Simple tab honors disabledTabs,
and both DnD wrappers drop their duplicated semantic-view mode rules in favor
of the shared picker-capability adapter. Filter subject and Sort by entry
points are covered explicitly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…overage

Keep Saved-mode metric selection reachable for saved-only semantic views now
that Simple is disabled, and lock in unchanged behavior for tables and for
semantic views that declare ADHOC_COLUMN_EXPRESSIONS (expression-based
Simple/Saved classification, defaults, search, Save, no failure warning).
Strengthen the apache#41456 backend assertions so temporal semantic dimensions keep
expression=None alongside the new feature serialization.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…apability boundary

Explain the opt-in SemanticViewFeature declaration, why the provider registry
key is deliberately not published (the @semantic_layer decorator prefixes
extension IDs), and the rule that generic picker components consume
ColumnPickerCapabilities rather than provider metadata.

Also address accessibility lint: the inline invalid-selection feedback uses an
output element for its implicit status role, and the compatibility warning
relies on Alert's native alert role.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 83fbfa1
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a68d2b2c60e21000879df31
😎 Deploy Preview https://deploy-preview-42540--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added doc Namespace | Anything related to documentation packages labels Jul 28, 2026
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.43%. Comparing base (7b767f6) to head (83fbfa1).
⚠️ Report is 54 commits behind head on master.

Files with missing lines Patch % Lines
...ols/DndColumnSelectControl/ColumnSelectPopover.tsx 93.82% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42540      +/-   ##
==========================================
+ Coverage   65.27%   65.43%   +0.16%     
==========================================
  Files        2795     2802       +7     
  Lines      157835   158614     +779     
  Branches    36080    36140      +60     
==========================================
+ Hits       103027   103795     +768     
- Misses      52816    52824       +8     
- Partials     1992     1995       +3     
Flag Coverage Δ
hive 38.29% <100.00%> (-0.02%) ⬇️
javascript 71.49% <95.61%> (+0.13%) ⬆️
mysql 57.58% <100.00%> (-0.01%) ⬇️
postgres 57.61% <100.00%> (-0.01%) ⬇️
presto 40.20% <100.00%> (-0.02%) ⬇️
python 59.02% <100.00%> (-0.01%) ⬇️
sqlite 57.25% <100.00%> (-0.01%) ⬇️
superset-extensions-cli 90.57% <ø> (?)
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc Namespace | Anything related to documentation packages review:draft size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants