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
driver-sql / driver-turso (SQLite faces): GLOB still cuts a STORED value at its first U+0000, so a NUL-free $contains / $notContains / $icontains / $endsWith answers wrongly on it; a $like / $ilike pattern holding U+0000 is cut the same way #20024
Filing gate: ① a defect with a named landing site on the SQLite faces:
packages/drivers/driver-sql/src/sql-driver.tstextMatchPredicate (the GLOB arm) and likePatternPredicate (the $like / $ilike SQLite arm), inherited by SqliteWasmDriver and TursoDriver local mode;
Filed by the domain:engine execution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) from the out-of-scope findings of its #19999 dev (os-dev-report on #19999, PR #20019). Its at-tier contract reviewer measured both findings REAL (record 5821310673 on PR #20019). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.
What happens
SQLite's glob() reads both its pattern and the stored value as C strings, so each is cut at its first U+0000. #19999 (PR #20019, in review) moves a comparand that HOLDS U+0000 off GLOB. Two cuts remain:
A stored value holding U+0000, filtered by a comparand without one.GLOB sees only the part before the value's first U+0000. Measured at PR fix(driver-sql, driver-turso): compare a SQLite text comparand holding U+0000 whole instead of cutting it at GLOB #20019's head b60884a47 on better-sqlite3 (SQLite 3.53.4), sql.js (3.49.1) and a real @libsql/client engine (3.45.1), with identical answers on all four faces (SqlDriver, SqliteWasmDriver, TursoDriver local and remote), 22 wrong cases per face in the reviewer's probe:
$contains: 'b' misses a value stored as 'a'+U+0000+'b';
A $like / $ilike pattern holding U+0000. It is cut at the U+0000. Measured on SqlDriver/better-sqlite3 at the same head, over the values 'a'+U+0000+'b', 'ab'+U+0000, U+0000+'z', 'plain' and '':
$like: '%'+U+0000 returned all five rows (JS: 'ab'+U+0000 only);
$like: U+0000+'%' returned '' and U+0000+'z' (JS: the latter only);
$ilike: '%'+U+0000+'B' returned all five (JS: 'a'+U+0000+'b').
The remote pushLikePattern shares the arm by reading; it was not executed.
Nothing raises: the filter answers a different question than it names.
For item 2, the caller's pattern carries wildcards, so instr does not apply. Whether to refuse a $like pattern holding U+0000 (INVALID_FILTER / 400) or compile it another way is the implementing round's measured choice.
Declare every face per references/compile-surfaces.md.
Filing-gate answers
Class: ① (defect, named landing site); finding class (a), measured by the dev and confirmed by the seat's at-tier reviewer.
Filing gate: ① a defect with a named landing site on the SQLite faces:
packages/drivers/driver-sql/src/sql-driver.tstextMatchPredicate(theGLOBarm) andlikePatternPredicate(the$like/$ilikeSQLite arm), inherited bySqliteWasmDriverandTursoDriverlocal mode;packages/drivers/driver-turso/src/remote-transport.tspushLike/pushLikePattern.Finding class (a).
Filed by the
domain:engineexecution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) from the out-of-scope findings of its #19999 dev (os-dev-reporton #19999, PR #20019). Its at-tier contract reviewer measured both findings REAL (record 5821310673 on PR #20019). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
SQLite's
glob()reads both its pattern and the stored value as C strings, so each is cut at its first U+0000. #19999 (PR #20019, in review) moves a comparand that HOLDS U+0000 offGLOB. Two cuts remain:A stored value holding U+0000, filtered by a comparand without one.
GLOBsees only the part before the value's first U+0000. Measured at PR fix(driver-sql, driver-turso): compare a SQLite text comparand holding U+0000 whole instead of cutting it at GLOB #20019's headb60884a47on better-sqlite3 (SQLite 3.53.4), sql.js (3.49.1) and a real@libsql/clientengine (3.45.1), with identical answers on all four faces (SqlDriver,SqliteWasmDriver,TursoDriverlocal and remote), 22 wrong cases per face in the reviewer's probe:$contains: 'b'misses a value stored as'a'+U+0000+'b';$endsWith: 'a'returns it;$notContains: 'b'returns it;$icontains: 'B'misses'A'+U+0000+'B';$contains: 'z'misses U+0000+'z'.$startsWithis provably unaffected. A stored U+0000 is reachable today: better-sqlite3 stores it, and so has sqlite-wasm since PR fix(driver-sqlite-wasm): a text value round-trips byte-for-byte — U+0000 no longer truncates it, a leading U+FEFF is no longer dropped on read #19998 (driver-sqlite-wasm: a text value does not round-trip the way driver-sql's does — an embedded NUL truncates the stored value, and a leading BOM is stripped on read #19978).A
$like/$ilikepattern holding U+0000. It is cut at the U+0000. Measured onSqlDriver/better-sqlite3 at the same head, over the values'a'+U+0000+'b','ab'+U+0000, U+0000+'z','plain'and'':$like: '%'+U+0000 returned all five rows (JS:'ab'+U+0000 only);$like:U+0000+'%'returned''and U+0000+'z'(JS: the latter only);$ilike: '%'+U+0000+'B'returned all five (JS:'a'+U+0000+'b').The remote
pushLikePatternshares the arm by reading; it was not executed.Nothing raises: the filter answers a different question than it names.
Suggested shape (⛔ not a ruling)
$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 dev and reviewer both recommend movingcontains/ends(with$notContainsand$icontains) to the length-aware constructs PR fix(driver-sql, driver-turso): compare a SQLite text comparand holding U+0000 whole instead of cutting it at GLOB #20019 introduces, for EVERY comparand, and keepingGLOBonly for$startsWithwithout U+0000.$contains/$endsWithplan moves. No index plan is lost, because a leading wildcard is never index-usable.LIKE|GLOBemitter markers in the existing driver-sql and remote text suites need respelling.endsconstruct must answer false, not NULL, on a zero-length stored value. PR fix(driver-sql, driver-turso): compare a SQLite text comparand holding U+0000 whole instead of cutting it at GLOB #20019 is fixing that now; without it,{ $not: { v: { $endsWith: 'x' } } }would lose the''row thatGLOBanswers right today.instrdoes not apply. Whether to refuse a$likepattern holding U+0000 (INVALID_FILTER/ 400) or compile it another way is the implementing round's measured choice.references/compile-surfaces.md.Filing-gate answers
domain:engine, the owner ofdriver-sqlanddriver-turso). It lands after PR fix(driver-sql, driver-turso): compare a SQLite text comparand holding U+0000 whole instead of cutting it at GLOB #20019 (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, same functions).closedincluded:SQLite GLOB stored value holding U+0000 NUL is cut, $contains $endsWith $notContains miss or match wrongly; $like pattern with NUL→ 2 hits: 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 (open; the comparand half, in flight) and service-analytics: all three SQL compilers emit a plain LIKE for the case-sensitive $contains family, which folds ASCII case on SQLite — the read scope and the native where admit rows the #4706 contract excludes #15684 (closed; the service-analytics LIKE fold). Neither covers the stored-value cut or$like.$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, the same mechanism.Dedupe words:
GLOB stored value U+0000 cut contains endsWith·sqlite text value NUL substring missed·$like pattern U+0000 GLOB·likePatternPredicate NULGenerated by Claude Code