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>
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>
`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>
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).
`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>
`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>
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
`__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.
`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>
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>