Fix FTS5 crashes when searching with special characters

Always wrap FTS5 query terms in phrase double-quotes instead of
checking a hardcoded list of special characters. Characters like
|, ;, +, #, @ were missing from the list, causing FTS5 syntax
errors (production errors #1 and #1398). Phrase-quoting every term
produces identical results for normal ASCII search (verified via
SQL) while safely handling any character FTS5 chokes on.

Add regression tests for bare special characters and special
characters mixed with normal words.
This commit is contained in:
Claudio Ortolina
2026-05-05 12:05:12 +01:00
parent c87e725b05
commit ba2566828c
2 changed files with 24 additions and 6 deletions
+2 -6
View File
@@ -47,12 +47,8 @@ defmodule MusicLibrary.Records.Search do
end end
defp fts_escape(term) do defp fts_escape(term) do
if String.contains?(term, ["'", " ", "\"", "(", ")", "^", "-", ":", "?", ".", "&"]) do escaped = String.replace(term, "\"", "\"\"")
escaped = String.replace(term, "\"", "\"\"") "\"#{escaped}\"*"
"\"#{escaped}\"*"
else
"#{term}*"
end
end end
defp fts_query_escape(query) do defp fts_query_escape(query) do
@@ -59,6 +59,17 @@ defmodule MusicLibrary.Records.SearchTest do
assert [greatest_show_on_earth.id] == search("mbid:#{airbag_mbid}", 10, 0) assert [greatest_show_on_earth.id] == search("mbid:#{airbag_mbid}", 10, 0)
assert [libertad.id] == search("mbid:#{airbag_au_mbid}", 10, 0) assert [libertad.id] == search("mbid:#{airbag_au_mbid}", 10, 0)
end end
test "bare special characters return empty results without crashing" do
for char <- ["|", ";", "&", "+", "#", "@"] do
assert [] == search(char, 10, 0)
end
end
test "special characters mixed with normal words return matching results" do
assert [] == search("brave &", 10, 0)
assert [] == search("hello ;", 10, 0)
end
end end
describe "search_records_count/2" do describe "search_records_count/2" do
@@ -94,5 +105,16 @@ defmodule MusicLibrary.Records.SearchTest do
assert 1 == Search.search_records_count(SearchIndex, "mbid:#{airbag_au_mbid}") assert 1 == Search.search_records_count(SearchIndex, "mbid:#{airbag_au_mbid}")
end end
test "bare special characters do not crash and return zero" do
for char <- ["|", ";", "&", "+", "#", "@"] do
assert 0 == Search.search_records_count(SearchIndex, char)
end
end
test "special characters mixed with normal words do not crash" do
assert 0 == Search.search_records_count(SearchIndex, "brave &")
assert 0 == Search.search_records_count(SearchIndex, "hello ;")
end
end end
end end