feat(results): cap SELECT result rows (default 500) with a 100/500/1k/5k/10k selector#94
Merged
Merged
Conversation
…/5k/10k selector A normal SELECT no longer pulls every row over the wire — it fetches at most a selected cap (default 500). Hybrid mechanism per #86: ClickHouse stops cleanly server-side (`max_result_rows` + `result_overflow_mode='break'`), and a small client-side guard in `applyStreamLine` trims the block-boundary overage `break` can leave, flagging `result.capped`. - `src/state.js` — `KEYS.resultRowLimit` + `resultRowLimit` (default 500, read from localStorage), `RESULT_ROW_LIMIT_OPTIONS`, and a pure `normalizeRowLimit` that snaps a stored/selected value back to a known option. - `src/net/ch-client.js` — `runQuery` honors `o.resultRowLimit`, adding the cap params via the existing `extra` dict. Scope is decided by the caller (app.js passes 0 for EXPLAIN/PIPELINE/ESTIMATE, which also run as `Table` and so can't be told apart by format here). - `src/core/stream.js` — `newResult(fmt, rowLimit=0)` carries the cap + `capped`; `applyStreamLine` stops pushing past the cap and flags `capped`. Pure, 100%. - `src/ui/results.js` — a row-limit `<select>` after the view tabs (hidden for EXPLAIN), a "first N (capped)" badge in the stats row, and the display cap now follows the limit (`visCap`) so 10000 renders 10000 instead of the old 5000. - `src/ui/app.js` — `setResultRowLimit` (persist pref + re-run so a raise fetches more), and the run path passes the cap for normal SELECTs only. Tests added in the same change (state/stream/net/results) — per-file 100% gate holds; build clean; e2e green on Chromium/Firefox/WebKit. Reconciles CHANGELOG [Unreleased]. ADR-0001 unaffected (`resultRowLimit` is a plain field like theme/density, not a signal). Closes #86. Part of #68 (Phase 2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BorisTyshkevich
added a commit
that referenced
this pull request
Jun 30, 2026
Reconcile the multiquery feature with #94 (result-row-cap selector), which landed on main and overlapped the same surfaces: - ch-client runQuery now carries BOTH o.resultRowLimit (single-query cap) and o.params (multiquery cap + session_id). - results.js: kept the shared renderGrid extraction; it now takes a `cap` param so the main table honors the selectable limit (visCap) while the script-row pane uses the default. buildToolbar keeps the script branch (early return) and #94's row-limit selector + "capped" badge on the normal path. - app.js: run() passes resultRowLimit (EXPLAIN-exempt) and sessionParamsFor; kept setResultRowLimit alongside the multiquery explain guard. - CHANGELOG: both Added entries; tests: both suites kept. 1122 tests green; build OK; e2e green (39).
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 #86 (Phase 2 of the roadmap #68).
A
SELECTover a huge table used to pull every row over the wire (only the display was capped at 5000) — wasting bandwidth/memory and able to hang the tab. This adds a real fetch cap.Hybrid mechanism (the settled design in #86):
max_result_rows = N+result_overflow_mode = 'break'so ClickHouse stops cleanly at the cap (no error, no full pull; it stops after the block that crosses N).applyStreamLinetrims the block-boundary overagebreakcan leave and flagsresult.capped.UX
localStoragekey, like theme/splitters) — applies to all tabs, survives reload.app.js(it knowsexplainMode);ch-clientjust honors the caller's limit, since ESTIMATE also runs asTableand can't be told apart by format.Files
src/state.js—KEYS.resultRowLimit,resultRowLimit(default 500, from localStorage),RESULT_ROW_LIMIT_OPTIONS, purenormalizeRowLimit.src/net/ch-client.js—runQueryhonorso.resultRowLimitvia the existingextradict.src/core/stream.js—newResult(fmt, rowLimit)carries the cap +capped;applyStreamLinestops at the cap. Pure, 100%.src/ui/results.js— selector, capped badge,visCap()display cap.src/ui/app.js—setResultRowLimitaction; run path passes the cap for normal SELECTs only.Checklist
npm testpasses (per-file 100/100/100/100 gate holds)npm run buildsucceeds (single-filedist/sql.html, no new runtime dep)src/core/, network insrc/net/(injected fetch), DOM insrc/ui/CHANGELOG.md[Unreleased]updated (deployed HTTP surface unchanged — only query-string params added; nohttp_handlers.xml/README change)resultRowLimitis a plain field like theme/density, not a signal); roadmap Roadmap to 1.0.0 #68 Phase-2 box to tick on mergeVerification
app.test.jsexercises the realrun()path — assertsmax_result_rows/result_overflow_mode=breakreach the URL for a normal SELECT, the client guard trims overage,cappedis set, and EXPLAIN/ESTIMATE send no cap.results.test.jscovers the selector (renders/reflects/re-runs), the badge, andvisCap.npm run test:e2egreen on Chromium / Firefox / WebKit (39 passed).🤖 Generated with Claude Code