Skip to content

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

Description

@claude

Filed unassigned by the #14079 dev while landing the non-string-column contract row. Recording only — no severity asserted, routing is triage's. Dedup: one targeted MCP search_issues (2026-09-05) found #6518 (closed — the DRIVER family's fix) and nothing on the analytics compilers.

Measured

service-analytics' three SQL compilers emit a plain LIKE for the case-SENSITIVE $contains family, so on SQLite — where LIKE folds ASCII case unconditionally — they answer rows the #4706 Q2 = A contract excludes:

compiler { name: { $contains: 'acme' } } over FILTER_TEXT_ROWS on sql.js contract (FILTER_TEXT_CASES)
NativeSQLStrategy.buildFilterClause (the query's own where) ['1', '2']ACME Corp AND acme corp ['2']
compileScopedFilterToSql (read-scope-sql.ts, the RLS read scope) same shape: "t"."name" LIKE ? ESCAPE ? same
ObjectQLStrategy SQL echo prints the same LIKE same

Measured on 2026-09-05 through NativeSQLStrategy.generateSql executed on sql.js over the shared fixture (the #14079 suite text-operator-non-text-column.test.ts had to steer its control comparand away from the case axis because of it). $icontains is unaffected — those arms fold both sides with asciiLowerSqlExpr. On Postgres LIKE is case-exact, so the drift is per dialect: one analytics query, one read scope, two row sets chosen by the datasource.

Why it matters more on the read scope

#6518 moved the DRIVER family onto GLOB on the SQLite dialects (and CAST(… AS BINARY) on MySQL) for exactly this reason, and its header records the security reading: a read scope that ADMITS rows the policy's case-sensitive predicate excludes is over-reach (#3948), not a loose filter. read-scope-sql.ts states the same #3948 reading for its own LIKE escaping (#5567) but its LIKE keyword never got the #6518 treatment, and this package depends on no driver, so it cannot borrow textMatchPredicate.

What is NOT here

Escaping (% / _, #5567) is correct on all three compilers; the fold of $icontains (#6520) is correct on all three. Only the case-EXACTNESS of $contains / $notContains / $startsWith / $endsWith is dialect-dependent, and only on SQLite.

Related

#6518 (the driver family's GLOB fix — the same defect one layer up) · #4706 (Q2 = A: the $contains family is case-sensitive) · #5567 (the analytics compilers' escaping, where the #3948 reading is already written) · #14079


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions