Repository navigation
Support ClickHouse 24.8 meta-less result streams in Table view (limited support) #627
Description
Goal
Support ClickHouse 24.8 in a deliberately limited but data-safe mode when JSONStringsEachRowWithProgress / JSONEachRowWithProgress returns row records without a meta record.
The support contract for 24.8 is:
Query execution and faithful Table result display are supported. Features that require ClickHouse result-type metadata are not guaranteed on 24.8.
The primary invariant is: never discard query values because result metadata is unavailable.
Problem
applyStreamLine() currently learns both column names and ClickHouse types only from a {"meta":[...]} line. A row is converted to an array by mapping through result.columns:
if (json.meta) {
result.columns = json.meta.map((m) => ({ name: m.name, type: m.type }));
} else if (json.row) {
result.rows.push(result.columns.map((c) => row[c.name]));
}On the confirmed 24.8 servers below, no meta line arrives. result.columns therefore remains empty and every successful row becomes [], so the UI silently renders an empty result despite the wire response containing the data.
Confirmed live on pinned images:
24.8.14.39(OSS)24.8.14.10547.altinitystable
The exact first ClickHouse 25.x release that emits meta for these formats has not been established. This issue therefore targets the observed protocol behavior rather than guessing a version boundary.
Required behavior
Meta-first streams
Existing behavior must remain unchanged when meta arrives before rows:
- column names come from
meta; - ClickHouse types come from
meta; - rows are stored in metadata column order;
- existing typed Table/KPI/chart/log behavior remains unchanged.
Meta-less streams
When a row arrives while result.columns is empty:
- Establish columns from that row's keys, preserving
Object.keys(row)order. - Represent unavailable ClickHouse types with the existing string field using the explicit sentinel
type: ''. - Store the row values in that established column order.
- Store all subsequent rows using the same established column order.
- Do not wait or buffer rows in the hope that
metawill arrive later; the reproduced 24.8 stream reaches EOF without metadata.
Conceptually:
if (json.row) {
const row = json.row as Record<string, unknown>;
if (result.columns.length === 0) {
result.columns = Object.keys(row).map((name) => ({ name, type: '' }));
}
// existing row-cap behavior remains in force
result.rows.push(result.columns.map((column) => row[column.name]));
}The implementation may be structured differently, but the behavior above is required.
Degraded-functionality contract
type: '' means ClickHouse result type is unknown, not String and not an inferred type.
For a meta-less result:
- Table: supported. Column names and raw query values must be displayed faithfully.
- Type display / type-aware cell formatting: may fall back to generic/unknown behavior.
- Automatic chart role/type detection: not part of the 24.8 support guarantee.
- KPI type eligibility / tuple parsing: not part of the 24.8 support guarantee.
- Logs auto-detection: not part of the 24.8 support guarantee.
- Other consumers that require
column.typemust fail closed or use their existing unknown-type behavior; they must not throw, silently discard row data, or fabricate a ClickHouse type.
No value-based ClickHouse type inference is required or desired in this issue. In particular, numeric-looking strings, dates, UUIDs, decimals, large integers, enums, etc. must not be assigned synthetic ClickHouse types from their JSON representation.
Non-goals
This issue does not require:
- reconstructing full ClickHouse types from row values;
- an auxiliary metadata query;
- restoring KPI/chart/log feature parity on 24.8;
- identifying the exact first 25.x release containing the upstream metadata change;
- changing
Column.typeto optional.
Those can be considered separately if broader old-server support is later required.
Tests
Add regression coverage for the actual failure mode, not only row-before-meta:
- a stream containing one row and EOF with no
metaever arriving; - multiple meta-less rows, preserving the first row's column order and every row's values;
- precision-sensitive/string values remain byte-for-byte/value-for-value intact through normalization;
- row-cap behavior still applies on meta-less streams;
- normal
meta -> rowbehavior remains unchanged; - representative type-dependent consumers tolerate
type: ''without throwing or inventing typed behavior.
Re-run the existing live compatibility/spike harness against the pinned 24.8 images and verify ordinary Table query results now match expected values.
Documentation / support matrix
Once the regression is verified, document ClickHouse 24.8 as limited support in the support-matrix work (#71), using language equivalent to:
ClickHouse 24.8: Limited support — query execution and Table results are supported. Typed result features such as automatic charts, KPI interpretation, logs detection, and type-aware formatting may be unavailable because these servers do not provide result metadata in the streaming format used by SQL Browser.
Do not describe 24.8 as full-fidelity support.
Acceptance criteria
- Meta-less
rowrecords populate columns from row keys and preserve all row values. - Unknown result types are represented explicitly as
type: ''; no ClickHouse type is inferred from values. - Multiple rows with no
metathrough EOF render correctly in Table view. - Existing meta-first behavior and typed functionality on newer ClickHouse versions are unchanged.
- Type-dependent consumers do not crash or silently discard data when
type === ''. - Existing row-cap/progress/exception behavior is unchanged.
- Live regression verification passes on
24.8.14.39and24.8.14.10547.altinitystablefor ordinary Table queries. - Document the supported-browser matrix (browsers, ClickHouse versions, IdP requirements) #71/support documentation records 24.8 as limited, not full, support with the scope above.
- ADR-0005 / ADR-0005: adopt @clickhouse/client-web behind the SQL Browser transport adapter #585 evidence cross-references this issue as the production compatibility fix, independent of the rejected
@clickhouse/client-webadoption decision.
Background
This was discovered during the #585 / ADR-0005 @clickhouse/client-web validation spike. The current production transport and the official-client candidate reproduced the same incorrect empty-row behavior on 24.8, so this is a production compatibility defect rather than a client-library migration defect.
ClickHouse added metadata to the progress-bearing JSON row formats after the tested 24.8 line. Newer tested servers (26.3 / 26.6) provide metadata and do not require this fallback.
Relates to
- ADR-0005: adopt @clickhouse/client-web behind the SQL Browser transport adapter #585 /
docs/ADR-0005-clickhouse-web-client.md— discovery/evidence - Document the supported-browser matrix (browsers, ClickHouse versions, IdP requirements) #71 — supported ClickHouse version matrix and 24.8 limited-support announcement
- Roadmap to 1.0.0 #68 — roadmap to 1.0
Activity
Merge pull request #628 from Altinity/docs/585-adr-0005-methodology-a…
BorisTyshkevich commentedon Aug 12, 2026
Triage decision updated after discussion: ClickHouse 24.8 will be supported in a limited, data-safe mode.
The implementation target is now explicit in the issue body:
type: '';metastream and live verification on the pinned 24.8 OSS and Altinity Stable images;This replaces the earlier conditional support-floor discussion: the product decision for this issue is to support 24.8 at Level 1 degradation.
[-]Query results silently render empty on ClickHouse servers predating the JSONEachRowWithProgress meta-line change (≤24.8)[/-][+]Support ClickHouse 24.8 meta-less result streams in Table view (limited support)[/+]BorisTyshkevich commentedon Aug 12, 2026
Ship log — #627
#627 — handoff
src/core/stream.ts(one production file). When arowarrives whileresult.columnsis empty, columns are established fromObject.keys(row)with the sentineltype: ''; every later row maps through that fixedorder.
meta, progress,pct, exception, cancellation, and row-cap behavior areunchanged. Live-verified on both pinned 24.8 digests, evidence committed under
docs/evidence/627/.fails closed on
''(parseClickHouseType('')→null,isNumericType('')→ false,chartStripType('')→'', logs regexes match nothing,isOptionColumnType('')→false, truthiness-gated type badge,
autoPanel→ Table). Criterion 5 is thereforesatisfied by regressions proving fail-closed behavior, not by new guards.
resolveLogsShapeis a deliberate exception. An explicitcfg.time/cfg.msgresolves by column name, never type, so an authored Logs panel now resolves against
a meta-less result (untyped string formatted as time) where
columnswas previouslyalways
[]. Permitted by the degraded-functionality contract — no throw, no discardedrows, no fabricated type — and now pinned by a test. The approved plan's audit called
itself exhaustive while covering only
autoPanel's auto-detect path; the conclusionheld, the exhaustiveness claim did not.
immutable historical digests; the ADR-0005: adopt @clickhouse/client-web behind the SQL Browser transport adapter #585 spike harness was retired in Epic: extract the Fetch-native ClickHouse client into a reusable package #630 Phase 8
(
280b54b).## ClickHouse server compatibilitysection and Document the supported-browser matrix (browsers, ClickHouse versions, IdP requirements) #71's body records the limited-support decision. Document the supported-browser matrix (browsers, ClickHouse versions, IdP requirements) #71 staysopen — its browser/IdP/mobile scope is untouched.
reshaping a track.
iso-altinityDocker network did notpre-exist in the sandbox and was created for the live run.
row-then-late-metastream would leave stored rowsbound to row-key order while headers flip to
metaorder. Unreachable —meta, whenpresent, is always the first line in both
…WithProgressformats; thejson.metaarm isuntouched; guarding it needs the extra
StreamResultstate the plan forbids. Pre-Support ClickHouse 24.8 meta-less result streams in Table view (limited support) #627 thesame sequence produced an empty row 0. No values are discarded either way.
result.columns.length. A zero-key{"row":{}}leftcolumns.length === 0ambiguousbetween "not established" and "established empty", so a later real row re-established
columns under an already-pushed zero-width row (verified:
rows = [[], ['1','2']]against2 columns). Held pre-Support ClickHouse 24.8 meta-less result streams in Table view (limited support) #627 and for meta-first streams; the fallback opened the hole. Fixed
in
edcb8bawithout addingStreamResultstate, sabotage-checked.https://chatgpt.com/c/6a7cc1bc-b294-83eb-ac4c-1d1df6e22d7f
in
edcb8ba(zero-key invariant;resolveLogsShapecoverage gap; 3 ADR-0005 passagesstill asserting the defect in the present tense). 1 further item examined and dismissed
with reasons (late-
meta, above).tests at the certified head
97d65a0; 7548 before the two review-pass tests landed),check-boundaries1899 files/10 rules, build); the live 24.8 OSS run re-executedindependently — captured stream byte-identical to the committed evidence,
version() = 24.8.14.39, zerometarecords; e2e Chromium green with one WebKitpage.gotoflake (a different test on each of two runs, spec passes 12/12 in isolation).pr-mode passes in one ChatGPT conversation (seeded from the plansession, so plan + review share one conversation) —
https://chatgpt.com/c/6a7cc1bc-b294-83eb-ac4c-1d1df6e22d7f
edcb8ba) → 1 accepted, 0 rejected:docs/evidence/627/README.mdpinned its"Tested commit" to the live-capture SHA and claimed the fallback was "unchanged since",
which
edcb8baitself falsified by changing the samejson.rowarm. Fixed ine94b7c5,which also added
tests/unit/evidence-627-replay.test.ts.fix(#627): support ClickHouse 24.8 meta-less result streams in Table view #674 (comment)
e94b7c5) → 2 accepted, 0 rejected, both caused by pass 1's own fix: a SHApinned as the branch's "final head" (in prose added by the commit after it), and a
"byte-identical" claim that is really deep structural equality. Fixed in
e9ca678.fix(#627): support ClickHouse 24.8 meta-less result streams in Table view #674 (comment)
97d65a0) → certified,VERDICT: SHIP, 0 accepted, 1 rejected (non-blocking:this PR body's own commit/test counts had gone stale — corrected by a body edit, which is
not a commit and so did not void certification).
fix(#627): support ClickHouse 24.8 meta-less result streams in Table view #674 (comment)
attemptsUsedwas 1 on every pass — no retry consumed an extra session slot.evidence-gated acceptance criterion asserts things about code state, so it goes stale the
moment a later commit touches the path it attests to. Correcting the prose twice was
treating symptoms. The structural fix is
tests/unit/evidence-627-replay.test.ts: itreplays the committed raw captures through the real
streamLines()->applyStreamLine()path at whatever commit
npm testruns, so a future decoder change now fails the suiteinstead of silently invalidating the evidence.
97d65a0then removed review chronology(including a
describename containing "PR review pass 1") from the permanent files, on thecoordinator's own initiative rather than from a finding — same class, third instance.
97d65a0; merged as3b6920874095a1ce97148d8d90d86741255ce5c2; 8/8 CI checks green at the certified head(
examplesskipped by path filter); findings 3 accepted / 1 rejected / 0 unresolved.fix(#627): address review pass 1 findings
fix(#627): address review pass 2 findings
docs(#627): state stable reasons in evidence replay spec and evidence…
Merge pull request #674 from Altinity/fix/627-metaless-stream-columns