Drill-to-Detail#
Drill-to-detail is a Superset feature that lets users right-click a chart data point in a dashboard and open a modal showing the underlying raw records that compose that aggregated value. It operates separately from Drill By (which re-groups the chart) and cross-filtering (which filters other charts on the page).
How It Works#
The flow has three stages:
-
Filter construction (frontend, per chart plugin): When a user right-clicks a data cell, the chart plugin builds a
BinaryQueryObjectFilterClause[]and passes it toonContextMenuunder thedrillToDetailkey. For example, the Table chart'shandleContextMenuiteratesfilteredColumnsMeta, skips metric columns, and emits one{ col, op: '==', val, formattedVal }entry per dimension column. -
Modal rendering (frontend, shared):
DrillDetailMenuItemsrenders the right-click submenu, displaying each filter'sformattedValas the label . Opening a menu item opensDrillDetailModal, which passesinitialFiltersdown toDrillDetailPane. The pane stripsformattedValbefore sending the request viagetDrillPayload, which serializes filters alongside the chart's existingtime_range,granularity, andextras. Filter tags shown inside the modal useformattedVal ?? val. -
Backend query: The payload hits the samples/data endpoint, where
get_sqla_queryinsuperset/models/helpers.pyresolves filter column names and builds the SQLWHEREclause.
Key Source Files#
| Layer | File |
|---|---|
| Table chart filter construction | plugin-chart-table/src/TableChart.tsx |
| Pivot table context menu | plugin-chart-pivot-table/src/PivotTableChart.tsx |
| Context menu items | Chart/DrillDetail/DrillDetailMenuItems.tsx |
| Modal wrapper | Chart/DrillDetail/DrillDetailModal.tsx |
| Data pane & filter state | Chart/DrillDetail/DrillDetailPane.tsx |
| Filter tag display | Chart/DrillDetail/DrillDetailTableControls.tsx |
| Payload builder | Chart/DrillDetail/utils.ts |
| Backend query builder | superset/models/helpers.py |
Known Bugs & Fixes#
1. Temporal columns: exact match instead of time-grain range (PR #39668)#
Symptom: Drilling to detail on a monthly-bucketed time column returned only records from the exact start timestamp of the bucket (e.g., only records from the first moment of the month), not all records within that month.
Root cause: The table chart emitted op: '==' with the bucket's start date. The backend matched that single timestamp rather than computing the full [start, end) range for the grain.
Fix (merged): PR #39668 updated TableChart.tsx to emit { op: 'TEMPORAL_RANGE', val: "<startISO> : <endISO>", grain: timeGrain } for temporal columns when a timeGrain is present, using a new getTimeRangeFromGranularity boundary calculation for all supported TimeGranularity values (seconds through years, including week variants).
2. Null temporal values converted to epoch date (PR #39668)#
Symptom: Drilling to detail on a cell with a null temporal value filtered for 1970-01-01 (Unix epoch) instead of rows where the column IS NULL.
Root cause: Null DateWithFormatter objects were coerced to a numeric value and formatted as an epoch date instead of being treated as absent.
Fix (merged): The same PR #39668 added a null check: if the cell value is null (including a DateWithFormatter wrapping null), emit { op: 'IS NULL', val: null } instead of attempting formatting.
3. Verbose column names silently drop the filter (PR #39766)#
Symptom: When a column's verbose_name (display label) differs from its physical column_name, right-clicking "Drill to detail by [Label]" returned the full unfiltered dataset — as if no filter had been applied.
Root cause: The frontend context menu handler passes the column's display label as the filter column identifier. get_sqla_query in helpers.py resolved filter columns exclusively by physical column_name; when the lookup failed, the filter was silently dropped rather than raising an error.
Fix (merged): PR #39766 added a verbose_name fallback in helpers.py: after the physical-name lookup misses, the code iterates self.columns to find a column where c.verbose_name == flt_col. Resolution order is now: column_name → verbose_name → metric name.
4. Null dimension label displays as blank in context menu (Issue #41677)#
Symptom: Right-clicking a null dimension value shows "Drill to detail by " with no label — instead of "Drill to detail by <NULL>".
Root cause: DrillDetailMenuItems renders filter.formattedVal directly. When the dimension value is null, formattedVal is empty/null, so the label is blank. The NULL_STRING constant (typically <NULL>) is not applied.
Status: Open as of 2026-07-02, with a community fix in progress.
Debugging Checklist#
When drill-to-detail returns unexpected results, check:
- Wrong row count / unfiltered results: Inspect the XHR request payload in browser DevTools. If a filter's
colmatches the column's display label rather than its physical name, theverbose_namebug (#39766) may be present even after the fix (e.g., if the verbose-name fallback was not yet deployed). - Temporal column returns one row / first-of-period only: Verify the filter
opin the request. If it is==rather thanTEMPORAL_RANGE, the temporal boundary fix (#39668) may not be applied. - Null cell produces epoch-date rows: Same PR (#39668); check that null values produce
IS NULLfilters. - Context menu label is blank for null values: Issue #41677 — cosmetic only, the underlying filter still fires (as
IS NULL), but the label in the submenu is empty.