fix: Cap query nesting depth and phrase expansion to prevent backend … - #37
Conversation
…crashes - Add `MAX_DEPTH` (1024): nested `(`/`!` plus chained within/pre/not-within operators deepen the parser's own recursion with no lexical nesting, so an unbounded chain overflows the stack and crashes the whole Postgres backend rather than erroring. - Add `MAX_PHRASE_EXPANSION` (16384): `<->`/`<N>` distribute an OR operand into every combination, so alternatives multiply along a chain and a couple hundred bytes of `(a|b) <-> (c|d) <-> …` can expand into gigabytes at parse time. - Mirror both caps in the pure SQL port (`_prox_p_depth_check`, the cross-product size check in phrase combination) so the two implementations reject the same queries. - Add Rust boundary tests for both caps and corresponding `capn*`/`capx*` cases in `tests/parity_cases.md`.
proxquery — pure-SQL port vs native extensionGenerated 2026-07-04 02:02:06Z · commit Context
Results — by query shapeAverage server-side ms/query over 1 run(s), no warmup (corpus already
Per-query breakdown — all 160 queries (counts + timings)
Parity (extension vs pure match counts on the same corpus —
Tokenizer vs simple (overlap overhead)Custom Unicode tokenizer (
Scaling by text lengthOne chained query (
Each column normalized to its shortest-length value, so the per-column growth rate is
Corpus & vocabulary
Plan — @~@ operator on a within shape (index-served via the a&b skeleton, not a seq scan)Plan — ts_prox_search on a boolean query (recheck folded away, Bitmap Index Scan) |
…crashes
MAX_DEPTH(1024): nested(/!plus chained within/pre/not-within operators deepen the parser's own recursion with no lexical nesting, so an unbounded chain overflows the stack and crashes the whole Postgres backend rather than erroring.MAX_PHRASE_EXPANSION(16384):<->/<N>distribute an OR operand into every combination, so alternatives multiply along a chain and a couple hundred bytes of(a|b) <-> (c|d) <-> …can expand into gigabytes at parse time._prox_p_depth_check, the cross-product size check in phrase combination) so the two implementations reject the same queries.capn*/capx*cases intests/parity_cases.md.