fix(dashboard): let CSV exports use query cache instead of always force-querying - #41469
Conversation
…ce-querying Two bugs caused dashboard CSV exports to always re-execute the query instead of using the cache (while the Explore page correctly used the cache): 1. Chart.tsx passed `force: true` to `exportChart()`, unconditionally bypassing the cache on every dashboard export. 2. buildQuery.ts coerced a missing `row_limit` to `0` via `|| 0` for download queries, while display queries left it as `undefined`. This produced a different cache key so exports would miss the cache even without the `force` flag. Both fixes are needed together: removing `force: true` lets the backend consult the cache, and preserving `undefined` for a missing row_limit ensures the export query produces the same cache key as the display query that populated the cache. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code Review Agent Run #f351e6Actionable Suggestions - 0Filtered by Review RulesBito filtered these suggestions based on rules created automatically for your feedback. Manage rules.
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
| moreProps.row_limit = | ||
| formDataCopy.row_limit != null | ||
| ? Number(formDataCopy.row_limit) | ||
| : undefined; |
There was a problem hiding this comment.
Suggestion: The new conversion only checks for null/undefined before calling Number, so non-numeric string values (for example legacy or URL-provided row_limit) become NaN here. That bypasses the normal buildQueryObject sanitization and injects an invalid row_limit into download queries, which can serialize differently from the display query and still miss cache (and may produce incorrect backend handling). Normalize invalid numeric values to undefined instead of passing through NaN. [cache]
Severity Level: Major ⚠️
❌ Table chart CSV/XLSX exports may still miss cache.
⚠️ Malformed row_limit values yield inconsistent export query objects.
⚠️ Edge-case exports may ignore intended row limits.Steps of Reproduction ✅
1. In a unit test, import `buildQuery` from
`superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts` (function defined around
line 57) and call it with a `TableChartFormData` object where `result_format: 'csv'`,
`result_type: 'results'`, and `row_limit: 'not-a-number'` (a non-numeric string), plus any
valid datasource/metrics.
2. During `buildQuery` execution, the callback passed to `buildQueryContext` computes
`isDownloadQuery` (lines ~75–78) which evaluates to `true` for `result_format: 'csv'`,
then enters the `if (isDownloadQuery)` block at line 229.
3. Inside that block (lines 230–233) the code sets `moreProps.row_limit =
formDataCopy.row_limit != null ? Number(formDataCopy.row_limit) : undefined;`. With
`row_limit: 'not-a-number'`, `formDataCopy.row_limit != null` is `true`,
`Number('not-a-number')` produces `NaN`, so the resulting query object in
`buildQueryContext(...).queries[0]` has `row_limit` set to `NaN`.
4. The same chart’s on-screen (display) query, built with identical form data but
`result_format: 'json'` / non-download settings, does not pass through this
`isDownloadQuery` block and instead uses the sanitized `row_limit` from the underlying
`buildQueryContext`/`QueryObject` creation, so its `row_limit` is a valid integer or
`undefined`, not `NaN`. Because the backend cache keys for SQLA datasets are derived by
passing the query object into `get_sqla_query` with `SQLA_QUERY_KEYS` including
`"row_limit"` (see `superset/connectors/sqla/models.py:2131–2137` and
`superset/models/helpers.py:197–202` where `"row_limit"` is listed), the export query
containing `row_limit: NaN` (serialized to `null`) produces a different cache key than the
display query’s `row_limit` (valid int or omitted), so CSV/XLSX exports for such malformed
`row_limit` values will bypass the populated cache and re-execute the query. Normalizing
invalid numeric inputs to `undefined` here would keep export and display query shapes
aligned and restore cache reuse.(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts
**Line:** 230:233
**Comment:**
*Cache: The new conversion only checks for null/undefined before calling `Number`, so non-numeric string values (for example legacy or URL-provided `row_limit`) become `NaN` here. That bypasses the normal `buildQueryObject` sanitization and injects an invalid `row_limit` into download queries, which can serialize differently from the display query and still miss cache (and may produce incorrect backend handling). Normalize invalid numeric values to `undefined` instead of passing through `NaN`.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
The flagged issue is correct. When Here is the corrected implementation for const rowLimit = Number(formDataCopy.row_limit);
moreProps.row_limit =
formDataCopy.row_limit != null && !Number.isNaN(rowLimit)
? rowLimit
: undefined;There are no other review comments in this pull request to address. superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #41469 +/- ##
==========================================
- Coverage 64.52% 64.52% -0.01%
==========================================
Files 2664 2664
Lines 146178 146180 +2
Branches 33722 33723 +1
==========================================
Hits 94319 94319
- Misses 50142 50144 +2
Partials 1717 1717
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
SUMMARY
Two bugs combine to make Dashboard CSV/XLSX exports always re-execute the query instead of serving from cache, while the Explore page correctly hits the cache.
Bug 1 —
Chart.tsx: TheexportChart()call in the dashboard chart component hardcodesforce: true, which tells the backend to bypass the cache entirely on every export. The Explore page does not set this flag, which is why it correctly uses the cache.Bug 2 —
buildQuery.ts: For download queries (result_format: 'csv'/'xlsx'),row_limitwas coerced to0viaNumber(formDataCopy.row_limit) || 0when the chart has no explicitrow_limit. Display queries leaverow_limitasundefined. This produces a different cache key for the export request versus the display request that populated the cache, so the export always misses — even after fixing Bug 1.Both fixes are required together:
force: truelets the backend consult the cache instead of always executing.undefined(instead of0) for a missingrow_limitensures the export query generates the same cache key as the display query that already populated the cache.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
Not applicable — backend cache behavior change with no UI change.
TESTING INSTRUCTIONS
row_limitvalues on the chart are still respected in the export.ADDITIONAL INFORMATION