You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec + drivers: a $like / $ilike pattern holding U+0000 is cut at the NUL on the SQLite faces and answers a different question; refuse it at every face through one spec predicate beside hasDanglingLikeEscape #20041
Filing gate: ① a defect with a named landing site: packages/spec/src/data/filter.zod.ts (a shared predicate beside hasDanglingLikeEscape), wired into the $like / $ilike refusal doors that already call hasDanglingLikeEscape:
Finding class (a): a measured wrong answer from a declared operator.
The domain:engine execution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) filed this to carry item 2 (i) of #20024, per the seat's ACCEPT on that card (5823530532). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.
What happens
$like / $ilike compile to GLOB on the SQLite faces: SqlDriver on better-sqlite3, SqliteWasmDriver on sql.js, and TursoDriver local and remote. SQLite's GLOB reads its pattern only up to the first U+0000, so a pattern holding U+0000 is cut there and matches a different set of rows. Nothing is raised.
Measured by the #20024 dev and reproduced by the seat's contract reviewer on PR #20038:
GLOB and LIKE both cut ('plain' LIKE '%'||char(0) is 1).
replace() cannot target U+0000, so a sentinel substitution still leaves 223 of 2392 cells wrong per engine.
instr() does not apply to a pattern with wildcards.
PR #20038 (#20024) moved $contains / $notContains / $icontains / $endsWith to length-aware constructs. $like / $ilike were left as they were, and its changeset states this limitation.
Suggested shape (⛔ not a ruling; the seat's direction on #20024, option A of the dev's four)
Add one spec predicate, "this $like / $ilike pattern holds U+0000", beside hasDanglingLikeEscape in packages/spec/src/data/filter.zod.ts.
Refuse such a pattern with INVALID_FILTER / 400 at every face that already refuses a dangling escape, so the accept set is one contract and not a per-dialect one. Also measure driver-mongodb's $like arm and bring it along if it answers.
Pin before and after on each face with the envelope (code + status + path), never a bare toThrow(). Keep the dangling-escape pins and every NUL-free $like pin green.
dangling LIKE escape refused at every face hasDanglingLikeEscape → 0 hits (the precedent landed without a card title naming it).
Dedupe words: like pattern holding U+0000 cut at GLOB · ilike NUL pattern answers a different question · refuse NUL like pattern every face · hasDanglingLikeEscape sibling predicate
Filing gate: ① a defect with a named landing site:
packages/spec/src/data/filter.zod.ts(a shared predicate besidehasDanglingLikeEscape), wired into the$like/$ilikerefusal doors that already callhasDanglingLikeEscape:driver-sqlsql-driver.ts;driver-tursoremote-transport.ts;driver-memorymemory-driver.ts/filter-refusal.ts.Finding class (a): a measured wrong answer from a declared operator.
The
domain:engineexecution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) filed this to carry item 2 (i) of #20024, per the seat's ACCEPT on that card (5823530532). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
$like/$ilikecompile toGLOBon the SQLite faces:SqlDriveron better-sqlite3,SqliteWasmDriveron sql.js, andTursoDriverlocal and remote. SQLite'sGLOBreads its pattern only up to the first U+0000, so a pattern holding U+0000 is cut there and matches a different set of rows. Nothing is raised.Measured by the #20024 dev and reproduced by the seat's contract reviewer on PR #20038:
$like: '%' + U+0000returned all 16 non-NULL probe rows, where@objectstack/formulareturns 4. This held on every SQLite face, at the base and at PR fix(driver-sql, driver-turso): SQLite $contains / $notContains / $icontains / $endsWith read the whole stored value, not only up to its first U+0000 #20038's head.GLOBandLIKEboth cut ('plain' LIKE '%'||char(0)is 1).replace()cannot target U+0000, so a sentinel substitution still leaves 223 of 2392 cells wrong per engine.instr()does not apply to a pattern with wildcards.PR #20038 (#20024) moved
$contains/$notContains/$icontains/$endsWithto length-aware constructs.$like/$ilikewere left as they were, and its changeset states this limitation.Suggested shape (⛔ not a ruling; the seat's direction on #20024, option A of the dev's four)
$like/$ilikepattern holds U+0000", besidehasDanglingLikeEscapeinpackages/spec/src/data/filter.zod.ts.INVALID_FILTER/ 400 at every face that already refuses a dangling escape, so the accept set is one contract and not a per-dialect one. Also measure driver-mongodb's$likearm and bring it along if it answers.policyread-scope subtree relays the policy field (and some comparands) in its 400: the #7929 redaction is not applied #20020 and driver-sql: eight more filter-compile refusal doors ignore the #8220 provenance mark, so a policy read scope refused there relays its field or comparand in the 400; close the class so every compile refusal goes through the withheld seam #20039).Clause-②: no (narrowing),minorwith BREAKING and an ADR-0087 marker. The precedent is PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971's changeset. Letcheck-changeset-no-majorandcheck-adr-0087-registrationdecide.code+status+ path), never a baretoThrow(). Keep the dangling-escape pins and every NUL-free$likepin green.GLOBstill cuts a STORED value at its first U+0000, so a NUL-free$contains/$notContains/$icontains/$endsWithanswers wrongly on it; a$like/$ilikepattern holding U+0000 is cut the same way #20024 and waits on the maintainer.Filing-gate answers
GLOBstill cuts a STORED value at its first U+0000, so a NUL-free$contains/$notContains/$icontains/$endsWithanswers wrongly on it; a$like/$ilikepattern holding U+0000 is cut the same way #20024 dev and confirmed by the seat's reviewer.domain:engine, the owner of the driver refusal doors). Thepackages/specpredicate travels with it, as the dangling-escape predicate did. It lands after PR fix(driver-sql, driver-turso): four filter-refusal doors read the provenance mark before naming a read scope field or comparand (#20020) #20037 (engine + driver-sql: seven filter-refusal doors ignore the #8220 provenance mark, so a refusal inside apolicyread-scope subtree relays the policy field (and some comparands) in its 400: the #7929 redaction is not applied #20020), which is editing the samedriver-sql/ turso-remote refusal doors now.closedincluded:like ilike pattern holding NUL U+0000 cut by GLOB refuse pattern every face SQLite→ 3 hits, all read: driver-sql / driver-turso (SQLite faces):GLOBstill cuts a STORED value at its first U+0000, so a NUL-free$contains/$notContains/$icontains/$endsWithanswers wrongly on it; a$like/$ilikepattern holding U+0000 is cut the same way #20024 (the positive control, the source card), service-analytics (SQLite): the shared text-match arm emitsGLOB, so a$contains/$endsWith/$startsWithcomparand holding U+0000 is cut at the NUL; on the read scope a leading U+0000 widens$contains/$endsWithto every row #20025 (service-analytics text arm) and driver-sql (SQLite faces): a$contains/$startsWith/$endsWithcomparand holding U+0000 is cut at the NUL byglob(), so the filter answers wrongly; one that starts with U+0000 makes$contains/$endsWithmatch every row #19999 (closed, comparand NUL). None covers$like/$ilikepatterns.$like pattern INVALID_FILTER refusal driver-memory driver-mongodb NUL character→ 16 hits (top 10 read): [finding]$newith an array comparand splits across backends: driver-sql and driver-memory refuse (400), driver-mongodb answers, formula matches every row — and both shared faces pass it #19886, driver-sql / driver-turso (SQLite faces):GLOBstill cuts a STORED value at its first U+0000, so a NUL-free$contains/$notContains/$icontains/$endsWithanswers wrongly on it; a$like/$ilikepattern holding U+0000 is cut the same way #20024, Filter AST:likeis folded to$containsat the wire — wildcards bind as literals and driver-sql'slike/ilikearm is unreachable #7536, [finding] driver-sql answers an equality-slot array NESTED under$and/$or/$notwith 500 DATABASE_ERROR, while the same shape at top level gets 400 INVALID_FILTER #19885, [finding] the comparand-SHAPE face declares it closes the door "for every driver at once", but an array in the IMPLICIT-EQUALITY slot passes it — anddriver-mongodbalone answers it, as an exact-array match #19757, [finding] driver-memory's own reference matcher has no$fieldarm — a cross-field comparand (bare or withaddDays) reaching it is presumably compared as a literal object rather than resolved or refused (grep reading, to be measured) #15104, [finding] driver-memory's matcher answers a NULL comparand inconsistently across the two readings of "no value" —$in:[null]/$nin:[null]disagree while$null/$ne:nullagree #13357, driver-memory's reference matcher matches a NULL-VALUED row against a well-formed bounded$between— the live mingo path excludes it, so one package answers one filter two ways #13549, driver-sql: an unresolvable WHERE column onaggregate()answers DATABASE_ERROR/500 wherefind()andcount()answer INVALID_FILTER/400 — the #8790 refusal never reached the third read door #11541 andcheck-driver-conformance.mjsFILTER_TEXT requirement-3 calls driver-mongodb's arm "the last place a barenew Errorescaped the ADR-0112 envelope" — it was not, and after #7047 the sentence names the wrong site #7159. None covers a NUL pattern.dangling LIKE escape refused at every face hasDanglingLikeEscape→ 0 hits (the precedent landed without a card title naming it).Dedupe words:
like pattern holding U+0000 cut at GLOB·ilike NUL pattern answers a different question·refuse NUL like pattern every face·hasDanglingLikeEscape sibling predicateGenerated by Claude Code