Part of #68 (Roadmap to 1.0.0).
Problem
A SELECT can return far more rows than anyone will scroll through, and today nothing caps the fetch — runQuery's streaming path accumulates every row (src/core/stream.js:28, src/net/ch-client.js:405). There's only a client-side display cap (VIS_CAP = 5000, src/ui/results.js:21): the rows are still all pulled over the wire, just not all rendered. A wide/huge result wastes bandwidth and memory and can hang the tab.
Proposed solution
Cap result rows with a default of 500 and a selector offering 100 / 500 / 1000 / 5000 / 10000.
Settled decisions:
- Mechanism: hybrid. Server-side
max_result_rows = N + result_overflow_mode = 'break' so ClickHouse stops cleanly at the cap (no error, no further data pulled), plus a small client-side guard that trims the block-boundary overage break can leave and flags the result as capped.
- Selector re-runs immediately — changing the limit re-runs the current query with the new cap (server-side, so raising it genuinely fetches more).
- Global, persisted preference — one localStorage pref (like theme/splitters), default 500, applies to all tabs, survives reload.
- Scope: normal result queries only (Table + explicit-
FORMAT SELECTs). EXPLAIN / PIPELINE / ESTIMATE are exempt — small output, and max_result_rows would truncate a plan oddly.
This is independent of #83's fixed 100-row cap for script (multiquery) SELECTs — different context, different cap.
Scope
src/state.js — add KEYS.resultRowLimit = 'asb:resultRowLimit' and resultRowLimit (default 500, read from localStorage at init). The option list [100, 500, 1000, 5000, 10000] as a constant.
src/net/ch-client.js — runQuery accepts o.resultRowLimit; when set (and not an EXPLAIN format), chUrl adds max_result_rows=<N> + result_overflow_mode=break via the existing extra dict.
src/core/stream.js — applyStreamLine (or the result model) tracks a row counter; stop pushing past the cap and set result.capped = true once the cap is reached. Pure, 100% covered.
src/ui/results.js — row-limit <select> in the result toolbar (after the view tabs, hidden for EXPLAIN views); on change, save the pref and re-run. Make the display cap follow resultRowLimit (so 10000 actually renders 10000, not the old fixed 5000). Show a "showing first N (capped)" badge in the stats row when result.capped.
src/ui/app.js — pass resultRowLimit: app.state.resultRowLimit into ch.runQuery; wire the selector's change → savePref + actions.run(); don't pass the cap for EXPLAIN runs.
Acceptance
Part of #68 (Roadmap to 1.0.0).
Problem
A
SELECTcan return far more rows than anyone will scroll through, and today nothing caps the fetch —runQuery's streaming path accumulates every row (src/core/stream.js:28,src/net/ch-client.js:405). There's only a client-side display cap (VIS_CAP = 5000,src/ui/results.js:21): the rows are still all pulled over the wire, just not all rendered. A wide/huge result wastes bandwidth and memory and can hang the tab.Proposed solution
Cap result rows with a default of 500 and a selector offering 100 / 500 / 1000 / 5000 / 10000.
Settled decisions:
max_result_rows = N+result_overflow_mode = 'break'so ClickHouse stops cleanly at the cap (no error, no further data pulled), plus a small client-side guard that trims the block-boundary overagebreakcan leave and flags the result as capped.FORMATSELECTs). EXPLAIN / PIPELINE / ESTIMATE are exempt — small output, andmax_result_rowswould truncate a plan oddly.This is independent of #83's fixed 100-row cap for script (multiquery) SELECTs — different context, different cap.
Scope
src/state.js— addKEYS.resultRowLimit = 'asb:resultRowLimit'andresultRowLimit(default 500, read from localStorage at init). The option list[100, 500, 1000, 5000, 10000]as a constant.src/net/ch-client.js—runQueryacceptso.resultRowLimit; when set (and not an EXPLAIN format),chUrladdsmax_result_rows=<N>+result_overflow_mode=breakvia the existingextradict.src/core/stream.js—applyStreamLine(or the result model) tracks a row counter; stop pushing past the cap and setresult.capped = trueonce the cap is reached. Pure, 100% covered.src/ui/results.js— row-limit<select>in the result toolbar (after the view tabs, hidden for EXPLAIN views); on change, save the pref and re-run. Make the display cap followresultRowLimit(so 10000 actually renders 10000, not the old fixed 5000). Show a "showing first N (capped)" badge in the stats row whenresult.capped.src/ui/app.js— passresultRowLimit: app.state.resultRowLimitintoch.runQuery; wire the selector's change →savePref+actions.run(); don't pass the cap for EXPLAIN runs.Acceptance
SELECTover a huge table fetches at most the selected cap (default 500) — verified by row count and that the stream stops (no full pull).max_result_rows).npm testgreen at the per-file coverage gate (state/stream/net/results changes at 100%).