Files
robot-clickhouseandClaude Fable 5 68886ff39a clickhouse-local builds for wasm64 and runs in Node.js and in the browser
The whole multicall `clickhouse` binary now compiles (11343 edges) and links
(a 236 MB wasm64 module) with Emscripten, and `clickhouse local` executes
queries correctly under Node.js >= 24 and in Chromium 149: MergeTree tables
on the in-memory filesystem, aggregations over 100 million rows of `numbers`
in ~2 seconds including engine startup, 1847 SQL functions, JSONEachRow from
stdin, Pretty formats. The CI job's pinned target becomes `clickhouse` and
the job provides the module as an artifact.

What it took, beyond the `dbms` step:

* `ENABLE_CAPNP` off for `OS_WASM`: its `kj` library calls `fallocate`
  unconditionally in its POSIX branch, which the Emscripten libc lacks.
* Executable link options for `OS_WASM` in cmake/target.cmake: an 8 MiB shadow
  stack and 256 MiB of growable memory (the defaults are 64 KiB and a fixed
  16 MiB, while `clickhouse` needs ~50 MiB for static data alone); `-g0` at
  the link, because binaryen's `wasm-opt` asserts in its DWARF bookkeeping
  (`AddrExprMap::add`) on a module with this much debug info;
  `-sPROXY_TO_PTHREAD` so `main`, which blocks its thread freely, runs on a
  pthread while the browser main thread keeps servicing the event loop that
  spawns the Web Workers backing new pthreads - without it the first blocking
  `pthread_create` deadlocks the page; and `-sEXIT_RUNTIME` so a finished
  query tears the runtime down.
* The `dlopen`-blocking override in programs/main.cpp yields on `OS_WASM`:
  Emscripten's libc unconditionally defines `dlerror`, so the override was a
  duplicate symbol - and a sandbox cannot load libraries in the first place.
* `initgroups` for `clickhouse-su`: the one libc symbol the entire binary
  found missing. Emscripten has the other identity stubs; this one fails
  closed with ENOSYS.
* Explicit `Field::safeGet<unsigned long>` and `ColumnVector::indexImpl<size_t>`
  instantiations extend from `OS_DARWIN` to `OS_WASM`: both are LP64 platforms
  where `uint64_t` is `unsigned long long`, so `size_t` matches no `UInt*` type.
* The `SignalListener` thread in `ClientApplicationBase` follows
  `OS_HAS_SIGNAL_HANDLERS`: without signals nothing ever writes to the signal
  pipe, and the blocking read of that pipe - all the thread does - fails with
  EAGAIN on Emscripten, which took the whole process down through
  `KillingErrorHandler`.

Known wart, upstream: after the runtime reaches `exit(0)` (verified with
`-sPTHREADS_DEBUG`), one Web Worker survives teardown and keeps the Node.js
process alive; the browser does not care.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 07:15:18 +00:00
..