Skip to content

fix(#465): validate a dashboard-variable tab's Run as a variable option query - #493

Merged
BorisTyshkevich merged 3 commits into
mainfrom
fix/variable-run-validation-465
Jul 27, 2026
Merged

fix(#465): validate a dashboard-variable tab's Run as a variable option query#493
BorisTyshkevich merged 3 commits into
mainfrom
fix/variable-run-validation-465

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

What & why

Closes #465.

#457 moved a Dashboard variable's option SQL into a dashboard-variable
main-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 ALL
column list that can't say which variable's branch was at fault.

Run on a dashboard-variable tab is now validated as a variable option query
end to end:

  • Every locally-detectable problem (blank/comment-only SQL, non-SELECT,
    more than one statement, FORMAT/INTO OUTFILE, a {name:Type} parameter,
    an optional /*[ … ]*/ block, an unterminated string/comment, the reserved
    batch-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.
  • A query that passes runs through the same bounded single-branch probe
    (compileOptionProbe) the option batch's own branches use, so Run can never
    accept SQL the batch would reject.
  • The response is then checked against the same shape the batch requires —
    exactly two non-nullable String-compatible columns (String,
    LowCardinality(String), FixedString(N)) — and a shape failure is never
    recorded as a successful run (no history entry, no bound-parameter
    recording, no detached-result source, no lastSuccessfulResultColumns
    update).
  • Ordinary query-tab Run behaviour is unchanged.

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/setExplainView
now refuse with a toast, mirroring the existing multi-statement-blocked
pattern.

See docs/ADR-0003-dashboard-viewing.md's #465 addendum for the full design
rationale (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) — green
  • npm run build — green
  • npm run test:e2e (chromium + webkit) — green, including two new Run
    cases (valid query displays rows; shape-invalid query reports the
    problem, never a successful result) added to the existing
    variable-tab fixture
  • Unit tests: restored compileOptionProbe/isOptionColumnType/
    validateOptionColumns in core/variable-options.test.ts;
    WorkbenchSession's new variable Run path (every acceptance-criteria
    bullet) in workbench-session.test.ts; the Explain-guard fix in
    app.test.ts

Checklist

  • npm test passes (the per-file coverage gate is non-negotiable)
  • Tests added/updated in the same change as the code
  • npm run build succeeds (single-file dist/sql.html)
  • Layers kept honest: pure logic in src/core/, network in src/net/ (injected fetch), DOM in src/ui/
  • No new runtime dependency
  • CHANGELOG.md ([Unreleased]) updated
  • Reconciled affected tracked work: roadmap Roadmap to 1.0.0 #68 (#457's follow-up line),
    docs/ADR-0003-dashboard-viewing.md (new #465 addendum)

🤖 Generated with Claude Code

https://claude.ai/code/session_011sMwR4BHM85MExSBYXzTYj

BorisTyshkevich and others added 3 commits July 27, 2026 11:25
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate dashboard-variable option SQL on editor Run

1 participant