From eea0e63ea8faa01332d3ff3453ebb92293bc08c1 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 28 Aug 2025 09:44:00 +0300 Subject: [PATCH] Account for single quotes in search strings According to the SQlite docs: Strings that include any other characters must be quoted. Characters that are not currently allowed in barewords, are not quote characters and do not currently serve any special purpose in FTS5 query expressions may at some point in the future be allowed in barewords or used to implement new query functionality. This means that queries that are currently syntax errors because they include such a character outside of a quoted string may be interpreted differently by some future version of FTS5. --- lib/music_library/records/search_parser.ex | 29 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/music_library/records/search_parser.ex b/lib/music_library/records/search_parser.ex index b8cde5d0..793626bc 100644 --- a/lib/music_library/records/search_parser.ex +++ b/lib/music_library/records/search_parser.ex @@ -86,6 +86,8 @@ defmodule MusicLibrary.Records.SearchParser do {:ok, %{query: ""}} iex> MusicLibrary.Records.SearchParser.parse("type:album") {:ok, %{type: :album}} + iex> MusicLibrary.Records.SearchParser.parse("Spock's Beard") + {:ok, %{query: "Spock''s Beard"}} """ def parse(""), do: {:ok, %{query: ""}} @@ -108,16 +110,26 @@ defmodule MusicLibrary.Records.SearchParser do defp normalize(result) do Enum.reduce(result, %{}, fn {:artist, [{:query, [value]}]}, acc -> - Map.update(acc, :artist, value, &(&1 <> " " <> value)) + Map.update( + acc, + :artist, + escape_special_characters(value), + &(&1 <> " " <> escape_special_characters(value)) + ) {:album, [{:query, [value]}]}, acc -> - Map.update(acc, :album, value, &(&1 <> " " <> value)) + Map.update( + acc, + :album, + escape_special_characters(value), + &(&1 <> " " <> escape_special_characters(value)) + ) {:mbid, [{:query, [value]}]}, acc -> Map.put(acc, :mbid, value) {:genre, [{:query, [value]}]}, acc -> - Map.put(acc, :genre, value) + Map.put(acc, :genre, escape_special_characters(value)) {:format, [value]}, acc -> Map.put(acc, :format, value) @@ -126,7 +138,12 @@ defmodule MusicLibrary.Records.SearchParser do Map.put(acc, :type, value) {:query, [value]}, acc -> - Map.update(acc, :query, value, &(&1 <> " " <> value)) + Map.update( + acc, + :query, + escape_special_characters(value), + &(&1 <> " " <> escape_special_characters(value)) + ) _, %{} -> %{query: ""} @@ -135,4 +152,8 @@ defmodule MusicLibrary.Records.SearchParser do acc end) end + + defp escape_special_characters(value) do + String.replace(value, "'", "''") + end end