fix(tesseract): align bridge member SQL with JS proxy reference#10850
Conversation
Replace the buggy regex (`function\s+\w+\(([A-Za-z0-9_,]*)|...`) that
choked on whitespace inside named function parameter lists, default
values, rest spread, destructuring, and anonymous function expressions.
The new parser strips the `async`/`function [*] [name]` prefix with a
small regex and then walks the parameter list with a balanced scanner,
so cases like `(x = f())` are handled correctly. Token normalisation
covers defaults (top-level `=`), rest (`...`), and `{}`/`[]`
destructuring via identifier extraction.
Unskip the previously known-bad cases in the bridge test suite and add
inline unit tests for the new helpers in `cubenativeutils`.
Apply the JS truthy short-circuit from `contextSymbolsProxyFrom` in the
bridge: undefined / null / "" / 0 / NaN / false collapse to a `1 = 1`
filter (and trip `requiredFilter` to throw), instead of being bound as
real values. Array params now go through `to_array().to_vec()` plus a
per-element scalar coercion that accepts strings, numbers, and booleans,
so number arrays produce the expected `IN (...)` clause instead of
erroring out as "Invalid param for security context".
Also drop the stray space from the array `toString` separator
(`.join(", ")` → `.join(",")`) to match the JS reference
(`paramValue.map(allocateParam).join(',')`).
The bridge's `security_context_to_string_fn` used to pre-bake the leaf's
toString output via `to_string_fn(allocated)` — every leaf-proxy
construction (i.e. every property access) eagerly registered the value
in `proxy_state`, even when the user only called `.filter()` or
`.unsafeValue()`. That double-pushed values, started `{sv:N}` at 1
instead of 0, and made `unsafeValue()` look like it allocated a
placeholder when the JS reference clearly does not.
Move the allocation inside a `make_vararg_function` closure so the
placeholder is only registered when the proxy is actually coerced via
`${...}`, matching `String(allocateParam(paramValue))` in
`contextSymbolsProxyFrom`. Each coercion gets its own placeholder, in
line with JS `toString` semantics. Array leaves now also use
`to_array().to_vec()` plus the existing `coerce_scalar_to_string`
helper, so non-string element types (numbers, booleans) work the same
way they do in the `.filter` path.
The bridge tests are reorganised to drop the now-stale "known
divergences from JS reference" block (the divergences are gone),
parametrise the falsy `.filter` / `.requiredFilter` matrix, and pin
two contract details that fall out of the lazy switch: empty-array
direct interpolation renders to `""`, and capturing a leaf proxy in a
local and coercing it twice now produces two distinct placeholders.
The multi-arg assertion is updated to match single-registration.
`convert_to_string` only handled JsString and null and then fell back to calling `toString` as a struct method, which errored out as "Object is not the Struct object" when a member SQL function returned a primitive like `42` or `true`. The JS reference does no coercion at the bridge boundary — the value flows through `resolveSymbolsCall` verbatim and the template literal in the consumer does `String(value)`. Add JsNumber and JsBoolean branches to `convert_to_string` so primitive returns coerce to their JS string form (`'42'`, `'1.5'`, `'true'`, `'false'`) and the bridge no longer rejects them. Update the bridge result-shape tests accordingly: the two cases that pinned the error are replaced with the JS-equivalent assertions (and broadened to cover both integer/decimal and true/false).
Four spots — `convert_to_string`, `coerce_scalar_to_string`, and the two SECURITY_CONTEXT scalar cascades — formatted whole f64 values via `n as i64`. That saturates above `i64::MAX`, so `9e18` collapsed to `"9223372036854775807"` instead of `"9000000000000000000"`. Drop the special case: `f64::Display` already prints integers without a trailing `.0` and matches JS `String(n)` for the cases the bridge cares about. The fix collapses the four duplicated formatting blocks back to the same one-liner, so future tweaks land in one place by default.
|
Claude finished @waralexrom's task in 2m 30s —— View job Code Review: PR #10850 — fix(tesseract): align bridge member SQL with JS proxy reference
Overall AssessmentThis is a well-crafted PR that fixes 5 distinct categories of divergence between the Rust bridge and the JS reference implementation. The changes are logically decomposed into clean commits, thoroughly tested, and the PR description is exceptionally detailed. All 12 previously-skipped tests are now active and passing with the correct expected values. Highlightsargs_names parser rewrite — The new balanced-paren scanner + top-level split approach ( Lazy placeholder allocation — The shift from eager JS truthy short-circuit — The Primitive return coercion — The f64 stringification — Dropping the Issues & SuggestionsSee inline comments for specifics. Summary of findings:
Test CoverageTest coverage is strong — all 12 previously-skipped cases are activated, and several new scenarios are added (numeric arrays, empty array leaf toString, fresh-placeholder-per-coercion, No security concerns identified — the changes correctly maintain the separation between Verdict: Looks good. The nits above are non-blocking. Nice work closing all 12 regression cases with clean, well-structured fixes. |
Pointers to specific identifiers from the legacy JS schema-compiler (`contextSymbolsProxyFrom`, `allocateParam`, the `String(...)` snippet, etc.) will rot once that code is fully deprecated. Rewrite the comments to describe the bridge behavior on its own terms — the WHY (lazy allocation, falsy short-circuit, primitive coercion vs. struct fallback) stays without naming the legacy site.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #10850 +/- ##
==========================================
+ Coverage 78.84% 78.86% +0.02%
==========================================
Files 470 470
Lines 92274 92289 +15
Branches 3433 3435 +2
==========================================
+ Hits 72751 72788 +37
+ Misses 19020 18999 -21
+ Partials 503 502 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Fixes the bridge layer that drives
MemberSql.compile_template_sqlso it matchescontextSymbolsProxyFrom/resolveSymbolsCallinCubeSymbols.ts. Closes the 12 regression cases that were sittingit.skipin the Tesseract bridge harness (recently introduced in #10838) — the bridge sweep now passes 80/80 with zero skips.Changes
function\s+\w+\(([A-Za-z0-9_,]*)|...) that choked on whitespace inside named arg lists, default values, rest spread, destructuring, and anonymous function expressions. New parser strips theasync/function [*] [name]prefix with a tiny regex, then walks(...)with a balanced scanner so cases like(x = f())are handled correctly. Token normalisation covers defaults (top-level=), rest (...), and{}/[]destructuring via identifier extraction.undefined/null/\"\"/0/NaN/falsecollapse to a1 = 1filter (and triprequiredFilterto throw), instead of being bound as real values. Array params now run throughto_array().to_vec()plus a per-element scalar coercion that accepts strings, numbers, and booleans, so number arrays produce the expectedIN (...)clause instead of erroring out as "Invalid param for security context". Drops the stray space from the arraytoStringseparator (.join(\", \")→.join(\",\")).to_string_fn(allocated)at construction time, so every property access eagerly registered the value, double-pushing values, starting{sv:N}at 1 instead of 0, and makingunsafeValue()look like it allocated a placeholder. Moved the allocation inside amake_vararg_functionclosure so registration happens only when the proxy is actually coerced via${...}, matchingString(allocateParam(paramValue))in the JS reference. Each coercion gets its own placeholder.convert_to_stringonly handledJsString/nulland then fell back to a struct-sidetoString, which errored as "Object is not the Struct object" when a member SQL function returned42ortrue. AddedJsNumber/JsBooleanbranches so primitives coerce to their JS string form ('42','1.5','true','false').n as i64shortcut in four spots;f64::Displayalready prints integers without a trailing.0and matches JSString(n)for the cases the bridge cares about, while not saturating abovei64::MAX.