mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2026-10-28 16:18:23 +08:00
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>