Skip to content

Commit dcd1870

Browse files
ahrzbclaude
authored andcommitted
docs(specializer): fold TASK-54's 12 fuzzer-found reject classes into the limitations contract
The known-limitations doc gains the fuzzer-found regex row (backrefs outside classes, the stacked-quantifier grammar, nested repetition products, whitespace bounds, class set-op lookalikes, Perl-class range endpoints, capturing {0}, anchor-only patterns) and a new 'how this document stays honest' section naming the three gate mechanisms (corpus replay, executable twin, standing fuzzer). The twin suite grows 9 assertions covering the new classes — 39 tests total. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 7418499 commit dcd1870

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

docs/known-limitations.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ wrong answer or require semantics we can't reproduce exactly:
8383
| `^` operator | It IS pow in DuckDB, but sqlparser's precedence differs from DuckDB's (`2*x^y` would parse as `(2*x)^y`). Mapping it computes the wrong tree silently. Use `pow()`. |
8484
| prefix `~`, `#`, `NOT GLOB` | Same class: precedence/parse divergences that would silently mis-associate. `xor()` covers bit-xor; `NOT (x GLOB p)` works. |
8585
| Regex reject list: `\B`, `\Q…\E`, `(?<name>…)`, duplicate group names, bounds > 1000, stacked quantifiers (`a*+`), `\u` escapes, negated Perl classes inside `[...]` | The RE2↔rust-regex differential battery (98 entries) proved these are the constructs where the engines disagree or DuckDB itself is broken (`\B` crashes DuckDB at runtime on non-ASCII). Everything else is byte-identical. |
86+
| Fuzzer-found regex rejects (TASK-54): `\1``\9` backrefs outside classes, the full stacked-quantifier grammar (`{2}*`, `?*`, `a???` — one lazy `?` is the only legal follower), nested repetition products > 1000, whitespace inside `{m, n}` bounds, class set-op lookalikes (`--`/`&&`/`~~`), non-POSIX `[` inside classes, Perl-class range endpoints (`[a-\d]`), capturing `(x){0}`, anchor-only multi-anchor patterns (DuckDB is SELF-inconsistent on these — its row path disagrees with its own constant fold) | The standing differential fuzzer (`tests/test_duckdb_regexp_fuzz.py`, in the normal gate) found these 12 classes in its first 3k-case deep run — each one a silent-wrong-answer risk in rust-regex — then re-swept to ZERO divergences over 40k cases across 8 seeds. Pins: `pins-waveB/fuzzer-task54.json`. |
8687
| `SIMILAR TO ... ESCAPE` | Not implemented in DuckDB itself. |
8788
| `* EXCLUDE (t.key)` on a USING join | DuckDB UNMERGES the coalesced column (it reappears at the right table's position) — measured, not modeled. Unqualified EXCLUDE works. |
8889
| `BETWEEN`/`IN` mixing non-numeric string literals with numbers | DuckDB converts at EXECUTION time (an empty input succeeds!); a bind-time conversion was measured to be over-eager. Numeric literals convert fine. |
@@ -128,3 +129,18 @@ whose message starts with a classification:
128129

129130
If a message you hit isn't in this document or the tests, that's a bug in
130131
our bookkeeping — file it.
132+
133+
## 7. How this document stays honest
134+
135+
Three mechanisms, all in the normal test gate:
136+
137+
1. **The corpus replay** (678 statements mined from DuckDB's test suite):
138+
every statement must match bit-for-bit, reject cleanly, or be a named
139+
divergence — a wrong answer anywhere fails the gate.
140+
2. **The executable twin** (`tests/test_known_limitations.py`): every
141+
limitation in this document is asserted; lifting one breaks a test.
142+
3. **The standing differential fuzzer** (`tests/test_duckdb_regexp_fuzz.py`):
143+
randomized DuckDB-vs-engine sweeps of the regex surface on every run
144+
(seed/size overridable for deep runs) — new divergences fail with the
145+
reproducing seed and SQL, and their fix lands as a reject-list entry
146+
plus a row in this document.

tests/test_known_limitations.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ def test_ubigint_static_payloads_reject():
133133
"duplicate regex capture group",
134134
),
135135
("SELECT regexp_matches(s, 'a{1001}') FROM __THIS__", "repetition bound"),
136+
# TASK-54 fuzzer-found classes (pins-waveB/fuzzer-task54.json):
137+
# each was a measured silent-wrong-answer risk in rust-regex.
138+
("SELECT regexp_matches(s, '(a)x\\1') FROM __THIS__", "backref"),
139+
("SELECT regexp_matches(s, 'a?*') FROM __THIS__", "quantifi"),
140+
("SELECT regexp_matches(s, 'a{2}*') FROM __THIS__", "quantifi"),
141+
("SELECT regexp_matches(s, '(a{100}){20}') FROM __THIS__", "repetition"),
142+
("SELECT regexp_matches(s, 'a{1, 3}') FROM __THIS__", "unsupported|parse"),
143+
("SELECT regexp_matches(s, '[a--b]') FROM __THIS__", "unsupported"),
144+
("SELECT regexp_matches(s, '[a-\\d]') FROM __THIS__", "Perl class endpoint"),
145+
("SELECT regexp_matches(s, '(x){0}') FROM __THIS__", "unsupported"),
146+
("SELECT regexp_matches(s, '^$') FROM __THIS__", "anchor-only"),
136147
# Not implemented in DuckDB itself.
137148
("SELECT s SIMILAR TO 'a' ESCAPE 'x' FROM __THIS__", "escape"),
138149
# Exec-time conversion order (an empty input succeeds in DuckDB).

0 commit comments

Comments
 (0)