diff --git a/superset-frontend/src/dashboard/actions/datasources.ts b/superset-frontend/src/dashboard/actions/datasources.ts index f48c12428459..1745bcbb5cc6 100644 --- a/superset-frontend/src/dashboard/actions/datasources.ts +++ b/superset-frontend/src/dashboard/actions/datasources.ts @@ -52,6 +52,8 @@ export function setDatasource(datasource: Datasource, key: string) { }; } +const inFlightDatasourceKeys = new Set(); + export function fetchDatasourceMetadata(key: string) { return (dispatch: Dispatch, getState: () => RootState) => { const { datasources } = getState(); @@ -61,8 +63,21 @@ export function fetchDatasourceMetadata(key: string) { return dispatch(setDatasource(datasource, key)); } + if (inFlightDatasourceKeys.has(key)) { + return undefined; + } + + inFlightDatasourceKeys.add(key); return SupersetClient.get({ endpoint: `/superset/fetch_datasource_metadata?datasourceKey=${key}`, - }).then(({ json }) => dispatch(setDatasource(json as Datasource, key))); + }) + .then(({ json }) => { + inFlightDatasourceKeys.delete(key); + return dispatch(setDatasource(json as Datasource, key)); + }) + .catch((err: unknown) => { + inFlightDatasourceKeys.delete(key); + throw err; + }); }; } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index c23f2578718d..1e4bbe3bfc55 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -53,6 +53,7 @@ import { onFiltersRefreshSuccess, setDirectPathToChild, } from 'src/dashboard/actions/dashboardState'; +import { fetchDatasourceMetadata } from 'src/dashboard/actions/datasources'; import { setHoveredChartCustomization, unsetHoveredChartCustomization, @@ -185,6 +186,28 @@ const FilterValue: FC = ({ const [isRefreshing, setIsRefreshing] = useState(false); const dispatch = useDispatch(); + // When a Date Range parent filter is active, the child filter needs + // granularity_sqla to apply the temporal WHERE clause. Read main_dttm_col + // from the Redux datasource cache as a fallback. + // + // The dashboard datasets API (/api/v1/dashboard/:id/datasets) only returns + // datasets used by chart slices, so native-filter-only datasets are never + // in state.datasources. Dispatch fetchDatasourceMetadata at mount time to + // populate the cache explicitly for those datasets. + const datasourceMainDttmCol = useSelector( + state => + datasetId != null + ? (state.datasources as Record)?.[`${datasetId}__table`] + ?.main_dttm_col + : undefined, + ); + + useEffect(() => { + if (datasetId != null) { + dispatch(fetchDatasourceMetadata(`${datasetId}__table`)); + } + }, [datasetId, dispatch]); + const { outlinedFilterId, lastUpdated } = useFilterOutlined(); const handleFilterLoadFinish = useCallback(() => { @@ -216,7 +239,11 @@ const FilterValue: FC = ({ groupby, adhoc_filters: adhocFilters, time_range: timeRange, - granularity_sqla: granularitySqla, + granularity_sqla: + granularitySqla || + (dependencies.time_range + ? datasourceMainDttmCol ?? undefined + : undefined), dashboardId, }); const filterOwnState = filter.dataMask?.ownState || {}; @@ -347,6 +374,7 @@ const FilterValue: FC = ({ dataMaskSelected, setHasDepsFilterValue, transitiveParentIds, + datasourceMainDttmCol, ]); useEffect(() => {