Verified against a real SQLite connection -- each of these returned zero rows
before and the right row after:
"Sarah's" before=[] after=["Sarah's Birthday"]
"100%" before=[] after=["Summer 100% Sale"]
"Gala_" before=[] after=["Gala_Night"]
Two bugs in one helper. It did .replace(/'/g, "''"), which is SQL string-quote
doubling -- meaningless and actively corrupting for a value that is bound, so
any search containing an apostrophe matched nothing. And its \% escaping had
no ESCAPE clause on the LIKE, which is engine-dependent: honoured on Postgres,
a literal backslash on SQLite, so % and _ stayed wildcards there.
Now mirrors the correct implementation from 59666b59: escape \ % _ only, and
a new likeWithEscape(column) emits `col LIKE ? ESCAPE '\'`. Both call sites
move to whereRaw with the value still bound; the column argument is a literal,
documented in the JSDoc.
Callers checked before changing the contract: adminPhotos.js,
adminEvents/crud.js, and sqlSecurity's own addLikeCondition(), which has no
callers anywhere -- pre-existing dead export, updated to the new shape rather
than deleted.
Behavioural change: searches containing ' % _ or \ now return the right rows
instead of nothing. Case sensitivity is unchanged.
Refs testplan REPORT.md, escapeLikePattern finding.