Skip to content

Specializer SQL support wave 2: LIKE/ILIKE + dynamic-table alias — corpus 240 → 265 (TASK-48) - #33

Merged
claude-agent-ahrzb[bot] merged 3 commits into
masterfrom
claude/specializer-sql-support-wave2
Jul 26, 2026
Merged

Specializer SQL support wave 2: LIKE/ILIKE + dynamic-table alias — corpus 240 → 265 (TASK-48)#33
claude-agent-ahrzb[bot] merged 3 commits into
masterfrom
claude/specializer-sql-support-wave2

Conversation

@claude-agent-ahrzb

Copy link
Copy Markdown

Wave 2, pins-first throughout (pins-wave1/pins_like.json, ~50 measured pin rows).

Shipped

  • LIKE / NOT LIKE / ILIKE with full ESCAPE semantics on both backends via one shared matcher: byte-based with codepoint _ (pinned against bytes, UTF-16 units, and graphemes), no normalization, backslash literal (no implicit escape — unlike Postgres). ESCAPE: any single byte, doubled = literal, the escape char never matches itself unescaped, '' = none, NULL = NULL, multi-byte operand traps with DuckDB's message. The data-dependent dangling-escape is reproduced exactly: plain false when the string is exhausted, a per-row SyntaxException only when the matcher examines it with bytes remaining. Our matcher is O(n·m) two-pointer restart — identical booleans and identical error rows to DuckDB's naive-recursive path (measured at 23s/row there on pathological patterns).
  • ILIKE = fold both sides with the measured simple casemap (verified equal to DuckDB lower() over an exhaustive all-codepoint sweep) then LIKE.
  • Dynamic-table alias: measured — the alias replaces the original name; assigning it as the binder's this_name reproduces the scoping in three lines. Column-renaming aliases reject by name.
  • Comma-join descoped by census, honestly: the corpus cases are self-joins of the dynamic table, not dynamic×static — the planned rewrite would have flipped ~0 cases. Recorded in the ticket.
  • SIMILAR TO rejects by name: DuckDB binds it to regexp_full_match (RE2), not SQL wildcards — mapping it as wildcards would be silently wrong.

The find of the wave

The corpus replay caught a live oracle divergence: DuckDB's ILIKE result for a NUL-containing row depends on its sibling rows — column statistics select an ASCII kernel (NUL-safe → TRUE) or the generic kernel (NUL-truncating fold → FALSE) for the identical row value. Statistics-dependent semantics are irreproducible by a row-at-a-time engine even in principle; this is now the repo's first documented oracle divergence (_KNOWN_DIVERGENT_SOURCES in the corpus replay, measurement attached in the pins).

Verification

  • ~10 new oracle test groups (the 22-column wildcard matrix, the 15-column ESCAPE matrix, unicode _, control chars, data-dependent error rows, vectorized column patterns); Slike fuzz-covered in the 500-seed differential.
  • Corpus: 240 → 265 of 678, zero FAILs; gate green (cargo 129 / pytest 683).

🤖 Generated with Claude Code

ahrzb and others added 3 commits July 26, 2026 17:42
…in) created In Progress

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nal name (TASK-48)

Measured: DuckDB makes qualified refs through the ORIGINAL name binder
errors once aliased; making the alias the binder's this_name reproduces
that scoping for qualified binds, star expansion, and messages with a
three-line change. AS/bare identical; column-renaming alias t(a,b)
rejects by name. Census note in the ticket: the comma-join rewrite was
DROPPED from wave scope — the corpus cases are self-joins of the
dynamic table, so the planned rewrite would flip ~0 cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… (TASK-48)

Byte-based matcher with codepoint _, leftmost-first two-pointer
backtracking: identical booleans AND identical error rows to DuckDB —
including the data-dependent dangling-escape (plain false when the
string is exhausted, per-row SyntaxException only when the matcher
examines it with bytes remaining) — at O(n*m) where DuckDB's own
column-pattern path is naive-recursive (measured 23s on one
pathological row). ESCAPE: any single byte, doubled = literal, the
escape char never matches itself unescaped, '' = none, NULL = NULL,
multi-byte operand traps with DuckDB's message. ILIKE folds both
sides with the measured simple casemap (== DuckDB lower(), exhaustive
sweep). The corpus replay caught a genuine oracle divergence: ILIKE
on NUL-containing rows is COLUMN-STATISTICS-dependent in DuckDB
(sibling rows change the result) — irreproducible row-locally, now
the repo's first documented divergence with the measurement attached.
SIMILAR TO rejects by name (binds to regexp_full_match in DuckDB).
Corpus: 240 -> 265 of 678, zero FAILs; gate green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude-agent-ahrzb
claude-agent-ahrzb Bot merged commit 68a1eac into master Jul 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant