@@ -48,6 +48,13 @@ def _rows(result) -> list[dict[str, Any]]:
4848 return [dict (zip (cols , r , strict = True )) for r in result .fetchall ()]
4949
5050
51+ def _like_escape (value : str ) -> str :
52+ """Escape LIKE metacharacters so a contributor search matches them literally. Without this `_`
53+ matches any character and `%` any sequence, so searching `a_il` returns `Anil` and `%` returns
54+ everyone. Paired with `ESCAPE '\\ '` at the call site."""
55+ return value .replace ("\\ " , "\\ \\ " ).replace ("%" , "\\ %" ).replace ("_" , "\\ _" )
56+
57+
5158def _prefixes (hashtag : str | list [str ]) -> list [tuple [str , str ]]:
5259 """Normalize one hashtag or many into deduped `(lo, hi)` prefix-range pairs, case-insensitive and
5360 order-preserving. Each hashtag matches as a prefix; the scope is the union across them."""
@@ -412,8 +419,8 @@ def leaderboard(
412419 rrel , rp = _recent_leaderboard (s , prefixes , start , end )
413420 search_pred , search_params = "" , []
414421 if q :
415- search_pred = " WHERE lower(name) LIKE ?"
416- search_params = [f"%{ q .strip ().lower ()} %" ]
422+ search_pred = " WHERE lower(name) LIKE ? ESCAPE ' \\ ' "
423+ search_params = [f"%{ _like_escape ( q .strip ().lower () )} %" ]
417424 con .execute (
418425 f"""
419426 CREATE OR REPLACE TEMP TABLE _lb_agg AS
0 commit comments