Commit 96349cc
fix(store): retry FTS5 parse errors with safe-quoted form (#209)
## Summary
Fixes #208 — `Store.Search` / `Store.SearchRanked` no longer surface
opaque `SQL logic error: ...` driver messages for adversarial input.
`rewriteQuery` short-circuits on FTS5 operator characters, passing input
verbatim to SQLite when any of `" : * ( OR AND NOT NEAR(` appears. Many
inputs that match that gate aren't valid FTS5 (`foo"`, `*`,
`"unterminated`, `foo:bar`) — SQLite raises a parse error that bubbles
to MCP clients as a 5xx-equivalent.
**Fix (issue option 2 — try-then-fall-back):** keep `rewriteQuery` and
its operator-passthrough contract unchanged; route `Search` /
`SearchRanked` through a new `queryFTS` that retries with safely-quoted
terms (`quoteAllTerms`) on FTS5 parse error.
- Preserves the advanced-query affordance for `label:foo`, `snap*`,
`"exact phrase"` (existing `TestRewriteQuery` rows still green).
- Adversarial input degrades to zero results instead of erroring.
- Retry trigger matches the `SQL logic error:` class modernc surfaces
FTS5 parse failures under (not `fts5: syntax error` — the actual driver
wording). The retry is idempotent for non-FTS5 logic errors, so real DB
issues still surface.
## Test plan
- [x] `TestQuoteAllTerms` — table-driven, covers `"` escape, single-char
ops, empty
- [x] `TestSearch_AdversarialInput` — 6 inputs from the issue table;
asserts `err == nil` for both `Search` and `SearchRanked`
- [x] `TestRewriteQuery` operator-passthrough contract preserved
(`label:snapshot`, `snap*`, `"exact phrase"`)
- [x] `make build`, `make test`, `gofmt -l`, `go vet ./pkg/store/...`
all green
Reviewed by `go-style-reviewer` (project subagent) + Codex (generic, via
`/forge ... codex`). Loop converged in 2 passes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent e378bf7 commit 96349cc
2 files changed
Lines changed: 96 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1183 | 1183 | | |
1184 | 1184 | | |
1185 | 1185 | | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
1186 | 1213 | | |
1187 | 1214 | | |
1188 | 1215 | | |
| |||
1246 | 1273 | | |
1247 | 1274 | | |
1248 | 1275 | | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
1249 | 1307 | | |
1250 | 1308 | | |
1251 | 1309 | | |
| |||
0 commit comments