12 Commits
Author SHA1 Message Date
Raúl MarínandClaude Opus 5.5 c838a0d03e Keep URL masking out of the WASM parser
Move the `SettingsSecrets.h` masking bodies into the .cpp so including the header does not pull
URL masking into every translation unit, and stub `renderSecretSettingValue` in the WASM build,
which formats with secrets shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:25:07 +00:00
Alexey MilovidovandClaude Fable 5 e9671f9b38 Remove ch_check: ch_parse subsumes it
The module has no consumers yet, so there is no compatibility to keep:
`ch_check` answered a strict subset of `ch_parse` (the same return
code, with the error message inside the JSON document instead of
alone), so it is gone rather than kept alongside. `ch_format` stays -
it is not a compatibility entry point but the only way to pretty-print
a query whose AST has no JSON form, such as `GRANT`.

Sizes re-measured (each configuration a few hundred bytes smaller);
the ceilings still hold at about 8% over. All four configurations pass
`ctest`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 21:32:58 +00:00
Alexey MilovidovandClaude Fable 5 5131ae496a Add a JSON API to the standalone WebAssembly parser
`ch_parse` parses a query and always answers with a JSON document. For
a query that parses: the AST (the same JSON the SQL function
`parseQueryToJSON` produces) and the syntax highlighting ranges. For
one that does not: the error message, its position (byte offset, line,
column), the expected-token list, and the highlights of the prefix that
did parse, so an editor keeps coloring while the user types.
`ch_format_json` is the reverse half, in builds with formatting: it
takes AST JSON - from `ch_parse` here or `parseQueryToJSON` on a server
- and formats it back into SQL.

`tryParseQuery` gains an optional `ParserDiagnostics` out-parameter (a
caller-provided `Expected`, which can have highlighting enabled, plus
the token the error message points at), so one parse yields the
canonical message and the machine-readable details together.

The error boundary now recovers anything deriving from
`Poco::Exception`, not just `DB::Exception`: the document handed to
`ch_format_json` is arbitrary, and `Poco::JSON::Parser` and the
`readJSON` overrides report a malformed one by throwing Poco exception
types (their `catch` blocks are dead under `-fignore-exceptions`).
The walk over the Itanium `__si_class_type_info` chain in `__cxa_throw`
is what establishes the derivation without libc++abi.

AST JSON goes with `-DENABLE_FORMATTING`: `writeJSON`/`readJSON` for
every node plus `Poco::JSON` and `Poco::Dynamic::Var` cost about 715 KB
raw / 172 KB brotli, while the errors-and-highlights half of `ch_parse`
costs about 2 KB and is in every build. Measured sizes and ceilings
updated accordingly; all four configurations pass `test.mjs`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:51:15 +00:00
Michael Stetsyuk 0e23153125 fix wasm 2026-08-18 09:41:26 +00:00
Alexey Milovidov 5b1433f94d Define the statics that master moved out of the headers
Merging `master` broke the standalone WebAssembly link:

    wasm-ld: error: undefined symbol: DB::ContextData::global_context_instance
    wasm-ld: error: undefined symbol: Fiber::getCurrentFiber()

`Fix every -Wunique-object-duplication site` moved `Fiber::getCurrentFiber` from the header into
the new `src/Common/Fiber.cpp` and turned `ContextData::global_context_instance` and
`ContextData::background_context_instance` from `inline static` members into ordinary ones defined
in `Interpreters/Context.cpp`. Both used to be emitted into whichever translation unit named them;
now they need a definition, and neither file is part of this build.

`Common/Fiber.cpp` joins the source list - it is a single thread-local pointer, and
`checkStackSize.cpp` already compiles `Common/Fiber.h` here. The two context instances are defined
in `wasm_runtime.cpp`, next to `Context::getAccessControl`, and stay null: there is no server
context in a browser, so `Context::getGlobalContextInstance` keeps taking its contextless branch.

Verified on ARM against a pruned wasi-sdk 33 matching the image: both configurations build and
pass both `ctest` tests - the default at 1193053 bytes (96947 under the ceiling) and
`-DENABLE_FORMATTING=OFF -DENABLE_DCL=OFF` at 778526 bytes (61474 under).
2026-08-03 12:53:21 +00:00
Alexey Milovidov f837f64b3e Merge remote-tracking branch 'origin/master' into wasm-parser-followups 2026-08-02 06:08:40 +00:00
Alexey MilovidovandClaude Opus 5 18a7708942 Do not require the patched libc++ to compile Common/Exception.cpp
`Exception.cpp` opens with `static_assert(STD_EXCEPTION_HAS_STACK_TRACE == 1)`,
and that macro comes from ClickHouse's patched libc++ in `contrib/libcxx-cmake`.
A build linking any other C++ standard library cannot compile the file at all -
and it is not only the assert: six places call
`std::exception::get_stack_trace_frames`, which only the patched libc++ has.
`utils/wasm-parser` works around this by leaving `Exception.cpp` out of its
build entirely and substituting stubs.

Move the two operations - reading the throw-site trace, and adopting one
exception's trace as another's - behind `getStackTraceOfThrow` and
`copyStackTraceOfThrow` in `Common/StackTrace.h`, so exactly one place has to
know whether the standard library records a trace at all. `base/defines.h`
defaults `STD_EXCEPTION_HAS_STACK_TRACE` to 0 when it is absent.

This stays fail-closed. Every currently supported platform builds
`contrib/libcxx-cmake` - `cmake/cxx.cmake` is included by every
`cmake/*/default_libs.cmake` - so `Exception.cpp` still hard-asserts the macro
is 1 on Linux, Darwin, FreeBSD and SunOS. Losing the definition there would
silently strip the stack trace from every exception, and it still cannot happen.

As a side effect this removes six copies of the same
`get_stack_trace_frames`/`get_stack_trace_size`/`__msan_unpoison` incantation.

No behaviour change: the refactored build produces byte-identical symbolized
stack traces, and `system.errors.last_error_trace` is populated exactly as
before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 05:36:19 +00:00
Alexey MilovidovandClaude Opus 5 cd5b667dda Build the standalone WebAssembly parser with CMake, and fix what had stopped building
`utils/wasm-parser/build.sh` compiled a hand-written list of sources with a hand-written
set of flags, piped the compiler's output through `grep error:`, and then linked whatever
objects happened to be there. On `master` today it produces nothing at all, in two
independent ways:

* `src/Core/Streaming/Settings.cpp` appeared the day before the build was merged and was
  never added to the list, so the link fails on `DB::WatermarkSettings::clone`. The script
  swallowed the compile errors that preceded it, which is how a source list drifts
  unnoticed.

* `-mllvm -wasm-enable-sjlj` on the link line does not reach the LTO backend, so `setjmp`
  and `longjmp` stay undefined. Passing it as `-Wl,-mllvm,...` does reach it - and then
  produces a module that links cleanly and throws an uncaught `WebAssembly.Exception` out
  of `ch_check` the first time a query reports its error by throwing. LLVM's WebAssembly
  `setjmp` lowering is only correct on a single translation unit, not on LTO-merged
  bitcode.

The build is now a CMake project of its own, cross-compiled with the toolchain file
wasi-sdk ships. CMake fails the build on a compile error rather than reporting it and
carrying on, tracks dependencies, and gives the two tests a home: `parser-cases` runs
`test.mjs` under `node`, and `parser-size` asserts a byte ceiling per configuration, since
this is what a browser downloads.

The `setjmp` boundary moves into `wasm_sjlj.c`, the one translation unit outside the LTO
unit, which is what makes the lowering correct again. It is C so that
`-fvirtual-function-elimination`, which assumes the LTO unit is the whole program, still
sees every virtual call. The frames in between are LTO'd and optimized exactly as before -
a WebAssembly exception unwinds through them on its own - so the module does not grow.

The source list also drops `Poco/Path.cpp`, which nothing in the closure needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 00:16:48 +00:00
Alexey Milovidov be78590f50 Port the standalone build's exception recovery boundary from #111971
The module aborted on any query the parser rejects by throwing rather than by
returning `false` - `SELECT sum(x) OVER (ROWS BETWEEN UNBOUNDED FOLLOWING AND
CURRENT ROW)`, for one - because `-fignore-exceptions` leaves nothing to catch.
`__cxa_throw` now jumps back to a `setjmp` boundary armed by `ch_check` and
`ch_format`, which report the message as a parse failure; the module stays
usable afterwards, which the test now checks. Recovery is gated on the thrown
type being exactly `DB::Exception`; anything else still stops the module.

This costs about 570 bytes and `-lsetjmp`, against the 262 KB and the
exception-handling proposal that `-fwasm-exceptions` would cost. `longjmp` runs
no destructors, so such a query leaks what the parser allocated below the
boundary - documented at `__cxa_throw`.

Ported from `parser-standalone-build` (ebca121740, 905c264506, 6c4bdaeaee),
which https://github.com/ClickHouse/ClickHouse/pull/112067 supersedes; flagged in
https://github.com/ClickHouse/ClickHouse/pull/112067#issuecomment-5125769942
2026-07-31 02:34:44 +00:00
Alexey Milovidov fe4d6f8a56 Make the WebAssembly exception shim type-safe
`__cxa_allocate_exception` returned `char` storage, which need not be aligned for the object
constructed in it, and `__cxa_throw` reinterpreted every thrown object as a `Poco::Exception` -
including `std::bad_alloc`, the one case the comment above it named. The storage is now
`alignas(std::max_align_t)`, and the `std::type_info *` argument decides: exactly `DB::Exception` is
read as such, anything else reports its type name and nothing more.
2026-07-28 08:01:34 +00:00
Alexey MilovidovandClaude Opus 5 5bce6cb607 Build the standalone parser without exception handling
`src/Parsers` has no `catch`, and a syntax error is reported by `tryParseQuery` returning null,
not by throwing. So in a standalone build an exception can only mean a bug or a resource limit,
and there is nothing to unwind to - the exception machinery is pure overhead.

The build now passes `-fignore-exceptions`, which compiles `throw`, `try` and `catch` but emits no
landing pads and no unwind tables. This needs no source changes at all - in particular no macro
over the `throw` keyword, which would have been undefined behaviour and would have mangled the
`throw()` exception specifications in Poco. What is left is a call to `__cxa_throw`, and defining
that in the WebAssembly runtime keeps libc++abi out of the bundle entirely: the exception object
is constructed, its message printed, and the module stops.

`CLICKHOUSE_PARSER_MINIMAL_BUILD` now has a job: `Common/StackTrace.h` is included by
`Common/Exception.h` and so by most of the tree, and it needs `<csetjmp>`, `<csignal>` and
`<ucontext.h>`, none of which exist without exception handling on WebAssembly. The parts that
need them are guarded out rather than making the header unavailable. Poco's `SignalHandler` is
guarded the same way, and `Thread_POSIX` stops referring to it.

Measured, `-Os`, stripped:

    2278794 -> 2016304 bytes,  618187 -> 569362 gzipped

and the module no longer needs an engine implementing the exception-handling proposal - it now
loads on stock Node 20 with no flags, where before it required `--experimental-wasm-exnref`.

Separately, and because they are an obsolete C++ feature in a library we own: every `throw()`
exception specification in `base/poco` is now `noexcept`. There were nineteen, plus two written
`throw ()`. None of them were dynamic specifications, so this is a straight rename.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 10:19:43 +00:00
Alexey MilovidovandClaude Opus 5 91ba199a47 Add a standalone WebAssembly build of the SQL parser
An experiment in how small `src/Parsers` gets on its own. The Web UI currently ships only
the lexer (`Lexer.cpp` builds to a 6 KB `.wasm` with no dependencies); a real parser would
give exact syntax errors with positions and query pretty-printing in the browser.

`utils/wasm-parser/build.sh` compiles 500 translation units with wasi-sdk and links a
2.7 MB module (789 KB gzipped) exporting a five-function C interface. `test.mjs` drives it
through `SELECT`, `CREATE TABLE`, window functions, recursive CTEs, `ALTER`, `INSERT`,
`SYSTEM`, `CREATE USER`, `GRANT`, and two deliberate syntax errors.

`-fwasm-exceptions` is required rather than optional: the parser reports errors by
throwing, and the wasi-sdk headers refuse `setjmp.h` without it.

`wasm_runtime.cpp` substitutes no-ops for the pieces a browser has no use for, each of
which would otherwise dominate the bundle. Stack traces are the sharpest case:
`Common/Exception.cpp` cannot compile against stock libc++ at all, because it
`static_assert`s on `STD_EXCEPTION_HAS_STACK_TRACE` from ClickHouse's patched libc++.
`Core/Settings.cpp` is left out because `ParserSetQuery` calls `Settings::castValueUtil`
once - only to ask whether a bare `SET x` names a `Bool` setting - and that single call
pulls in every `SettingField*Traits` specialization, more code than the parser itself.
The timezone database that `contrib/cctz-cmake` generates into the binary is left out
too, as is query masking.

`shim/` holds the POSIX headers wasi-libc omits (`netdb.h`, `ucontext.h`, `sched.h`,
`pwd.h`, `fenv.h` rounding modes, `sun_path`, `__u6_addr`). Nothing calls into them; they
exist so headers that name those types in signatures still compile.

The README records where the size goes. The largest remaining cut is access-entity DDL:
`CREATE USER`/`ROLE`/`QUOTA`/`ROW POLICY` and `GRANT` account for 334 KB of parser and
`Access` code, roughly a third of the bundle once the re2 and abseil they share with
`COLUMNS('regexp')` matchers is counted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 10:18:32 +00:00