fix(#465): validate a dashboard-variable tab's Run as a variable option query - #493
Merged
Merged
Conversation
…on query #457 moved a Dashboard variable's option SQL into a main-editor tab but deleted the drawer's Test action with it, so Run executed the SQL raw and reported a plain success even on the wrong column count or type — the mistake surfaced only later, as an unattributed Dashboard-wide option-batch failure. Restores compileOptionProbe/isOptionColumnType/validateOptionColumns in core/variable-options.ts and wires a new WorkbenchSession.runVariableSql: every locally-detectable problem is reported with no request sent, a valid query runs through the same bounded probe the option batch uses, and a shape-invalid response is never recorded as a successful run. Also guards the Explain button/view-switch, which would otherwise have silently ignored Explain on a variable tab instead of running it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011sMwR4BHM85MExSBYXzTYj
…ookkeeping
A pre-merge review caught two real issues in the previous commit:
- runVariableSql() sent the user's ordinary display cap (state.resultRowLimit)
and no params at all, instead of VARIABLE_OPTION_CAP + 1 and
{readonly: 2, max_result_bytes: VARIABLE_OPTION_BYTE_CAP} — the exact bound
and safeguards the option batch (runOptionBatch) and the removed Test path
both used. A display cap lower than the batch's own bound could hide a
max_result_bytes failure the full batch would hit later, and dropping the
byte cap entirely left Run able to pull an unbounded response.
- A genuinely valid run was excluded from History and detached-result source
capture, unconditionally. #465 only requires that a shape-INVALID response
not be mistaken for success; it never asked for a valid run's existing
affordances to be removed, and the analogy to saveVariableTab's History/
Library exclusion doesn't hold (that's about never creating a saved query,
not about a tab's own execution history — which an ordinary unsaved query
tab still records). Restored to match run()'s own success path, keeping only
the two fields still genuinely inert for this document kind unrestored:
bound-param recording (option SQL can have none) and
lastSuccessfulResultColumns (a variable tab has no Spec to feed it).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011sMwR4BHM85MExSBYXzTYj
Review finding: a dashboard-variable tab's option SQL was still gated
by the generic query-variable machinery. The Run button showed an
input strip and disabled itself for a {name:Type} reference instead
of leaving Run enabled and reporting optionSqlDiagnostics' own
message, and runVariableSql additionally re-gated on the whole
tab.sqlDraft via varGateBlocked, which could block a validated
selection over an unrelated unfilled variable elsewhere in the same
tab. Export had the same gap and worse: exportEntry never checked for
a variable tab at all, so it could stream raw, unvalidated option SQL
through its uncapped path, bypassing optionSqlDiagnostics and the
bounded probe transport entirely. Export is now blocked outright for
a variable tab, same precedent as Explain's existing
explainVariableBlocked guard.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011sMwR4BHM85MExSBYXzTYj
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Closes #465.
#457 moved a Dashboard variable's option SQL into a
dashboard-variablemain-editor tab, but deleted the drawer's Test action along with it — Run
executed the SQL raw and reported a plain success even when it returned the
wrong number or type of columns. The break surfaced later and further away:
only when the Dashboard-wide option batch failed, as one merged
UNION ALLcolumn list that can't say which variable's branch was at fault.
Run on a
dashboard-variabletab is now validated as a variable option queryend to end:
SELECT,more than one statement,
FORMAT/INTO OUTFILE, a{name:Type}parameter,an optional
/*[ … ]*/block, an unterminated string/comment, the reservedbatch-tag column) is reported with no request sent, before authentication —
and a multi-statement variable query is rejected by name rather than
silently routed to the ordinary script runner.
(
compileOptionProbe) the option batch's own branches use, so Run can neveraccept SQL the batch would reject.
exactly two non-nullable
String-compatible columns (String,LowCardinality(String),FixedString(N)) — and a shape failure is neverrecorded as a successful run (no history entry, no bound-parameter
recording, no detached-result source, no
lastSuccessfulResultColumnsupdate).
A review pass on this branch caught a second issue before it shipped: the
Explain button/view-switch would have silently ignored Explain on a variable
tab (dispatching straight into the new validated Run path with no indication
anything was ignored) instead of running it.
explainQuery/setExplainViewnow refuse with a toast, mirroring the existing multi-statement-blocked
pattern.
See
docs/ADR-0003-dashboard-viewing.md's#465addendum for the full designrationale (why the preflight lives ahead of the
run()/runScript()split,why a variable document still isn't a query, etc.).
Test plan
npm test(100/100/100/100 gate +tsc --noEmit) — greennpm run build— greennpm run test:e2e(chromium + webkit) — green, including two new Runcases (valid query displays rows; shape-invalid query reports the
problem, never a successful result) added to the existing
variable-tabfixturecompileOptionProbe/isOptionColumnType/validateOptionColumnsincore/variable-options.test.ts;WorkbenchSession's new variable Run path (every acceptance-criteriabullet) in
workbench-session.test.ts; the Explain-guard fix inapp.test.tsChecklist
npm testpasses (the per-file coverage gate is non-negotiable)npm run buildsucceeds (single-filedist/sql.html)src/core/, network insrc/net/(injected fetch), DOM insrc/ui/CHANGELOG.md([Unreleased]) updated#457's follow-up line),docs/ADR-0003-dashboard-viewing.md(new#465addendum)🤖 Generated with Claude Code
https://claude.ai/code/session_011sMwR4BHM85MExSBYXzTYj