feat: query variables ({name:Type}) with native param_ substitution (#134)#138
Merged
Conversation
…134) Detect ClickHouse typed placeholders `{name:Type}` while editing and show a single-line input strip below the editor toolbar (scrolls horizontally, hidden when none). Run is disabled until every detected variable has a value. On execution the values ride along as ClickHouse's native `param_<name>` query-string arguments, so the server substitutes them per the declared type (injection-safe; String/Identifier/DateTime/Array/Map all work) — the SQL text is sent unchanged. Substitution is gated on row-returning statements, so a `CREATE VIEW … {x:String} …` definition is stored verbatim (matching ClickHouse parameterized views). The gate + param-passing live at the execution choke points (run/runScript/export), so Run, ⌘↵, Explain, and Export all honor them. - New pure `src/core/query-params.js` (detect/read/paramArgs/missing/unfilled), 100% covered; lexer skips string literals + comments like sql-split.js. - Distinct from #39's `{{name}}` composable-query CTE-merge (different syntax). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014X92mUpk2ts56USdMGKaNa
…back)
Move variable values from per-tab, in-memory storage to a single shared,
name-keyed store on `state.varValues`, persisted to localStorage (asb:varValues):
- A value typed for `{database:String}` is reused — prefilled automatically —
by every query that references the same variable, no retyping.
- Values survive tab switches, loading queries from history/library, and page
reloads.
The variable strip prefills each input from the shared store and writes back
(persisting) on edit; the Run gate, run/runScript, and both export paths all read
the shared store. The strip's rebuild signature no longer keys on tab id, since
values are global by name.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014X92mUpk2ts56USdMGKaNa
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 #134.
Adds query variables: ClickHouse typed placeholders
{name:Type}are detected while editing, and a single-line strip below the editor toolbar shows one input per detected variable (it scrolls horizontally when there are many, and is hidden when there are none). Run is disabled until every variable has a value.On execution the entered values are sent as ClickHouse's native
param_<name>query-string arguments, so the server substitutes them per the declared type — injection-safe and type-correct (String/Identifier/DateTime/Array(…)/Map(…)all work). The SQL text itself is sent unchanged; the app never string-rewrites the query.Substitution is gated on row-returning statements only, so a
CREATE VIEW … {x:String} …definition is stored with its placeholder intact — which is exactly how ClickHouse parameterized views work. The gate and the param-passing live at the shared execution choke points (run/runScript/ export), so Run, ⌘↵, Explain, and Export all honor them.Design decisions (agreed on the issue)
param_*substitution, not client-side string rewriting.SELECT/WITH…SELECT/EXPLAIN); never DDL/CREATE VIEW.asb:varValues): a value typed once is reused/prefilled wherever the same variable appears — across queries, tabs, history/library loads, and reloads. (Updated from the original "per-tab, in-memory" decision per review feedback on the PR.)Distinct from #39's
{{name}}composable-query CTE-merge (different syntax and purpose) — noted in the roadmap and the issue.How it's built
src/core/query-params.js—detectParams(lexer skips string literals + comments and handles nested-paren types, mirroringsql-split.js),readStatementParams,paramArgs,missingValues,unfilledParams— 100% covered.src/ui/app.js— variable strip render, the sharedvarGateBlockedgate, andparam_*merge intorun/runScript/exportDirect/exportScript. No new dependency; rides the existing fetch seam.Follow-up (filed separately, not in this PR)
sql-split.jsandquery-params.js; a shared span-scanner would remove the duplication (and fix a rare edge — a}inside a quotedEnumtype label in the type position). Filed as Extract a shared SQL span-scanner for sql-split.js + query-params.js #139 (inbox).Checklist
npm testpasses (per-file coverage gate;query-params.js100/100/100/100,app.js100 lines/statements)npm run buildsucceeds (single-filedist/sql.html)src/core/, DOM insrc/ui/CHANGELOG.md([Unreleased]) updated🤖 Generated with Claude Code