Repository navigation
Unicode-aware case-insensitive LIKE matching (accented/non-ASCII characters) #222
Copy link
Copy link
Closed
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or request
Milestone
Description
Activity
- addedbugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or request
on Jul 25, 2026 - added 5 commits that reference this issue
on Jul 25, 2026 Shipped in v1.8.0 and confirmed still in place on current `main`.
# Status Requirement Method Verification 1 ✅ Raw SQLite `LIKE` is confirmed ASCII-only case-insensitive (canary) Unit test `SqliteQuoteServiceUnicodeSearchTests.RawSqliteLike_AccentedCharacters_IsCaseSensitive` 2 ✅ `UNICODE_CONTAINS` is registered on every connection from `SqliteConnectionFactory`, including a second connection Unit test `SqliteQuoteServiceUnicodeSearchTests.UnicodeContains_RegisteredOnEveryConnection` 3 ✅ `UNICODE_CONTAINS('café', 'CAFÉ')` returns true Unit test `SqliteQuoteServiceUnicodeSearchTests.UnicodeContains_MatchesAccentedCaseVariant` 4 ✅ With the flag off (default), `Search`/fuzzy filters do not match accented case variants Unit test `SqliteQuoteServiceUnicodeSearchTests.*_FlagOff_DoesNotMatchAccentedCaseVariant` — 8 tests 5 ✅ With the flag on, `Search`/fuzzy filters do match accented case variants Unit test `SqliteQuoteServiceUnicodeSearchTests.*_FlagOn_MatchesAccentedCaseVariant` — same 8 call paths 6 ✅ Every existing ASCII search/filter behaviour is unchanged with the flag off Unit test `SqliteQuoteServiceSearchTests` + `SqliteQuoteServiceConversationTests` — 60 tests total, all passing 7 ✅ `SqlQueryGuardTests.AssembledQueryCases` covers both flag states for all 8 sites Unit test Full solution passing 8 ✅ `unicode_aware_search` option present, schema'd, and translated identically in both `addon/` and `addon-beta/` Live Confirmed matching 9 ✅ T1: app starts in VS without error Live Confirmed by developer — clean startup stats, no errors 10 ✅ T2: toggle proven against a real container in both states Live Run against `quotinator:local`: flag-off → `NoResults`; flag-on → `Ok` with the fixture returned 11 ✅ README.md/CLAUDE.md documentation updated Live Confirmed Retroactive close — this shipped in v1.8.0 but was never formally closed.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingenhancementNew feature or requestNew feature or request
Background
SQLite's
LIKEoperator only case-folds ASCII by default — confirmed against SQLite's own docs during #216. Any accented Latin character (é/É), Cyrillic, Greek, etc. is matched case-sensitively unless the ICU extension is loaded. This is a property of SQLite's core C library itself, not of the .NET provider — the same limitation applies whether the project usedMicrosoft.Data.Sqlite(what Quotinator actually uses) orSystem.Data.SQLite.This was found as a borderline case during #216's audit and deliberately not fixed there — #216 is scoped to id/enum/name-lookup case-insensitivity (
LOWER(column) = LOWER(@param)exact matches), which is unaffected by this (SQLite'sLOWER()/UPPER()SQL functions are also ASCII-only, but exact-match comparisons don't depend onLIKE's own folding behaviour the way a partial/contains match does). This issue is only aboutLIKE-based partial/contains matching —/quotes/search'sq/fieldparameters,character/author/sourcefuzzy filters, and any future free-text search.Decision at filing time: deferred, not urgent — Quotinator currently has no translations that would exercise non-ASCII partial-match search in a way that's known to be broken, so the practical impact today is low. Filed to the maintenance milestone rather than left undocumented.
Two possible fixes
SQLITE_ENABLE_ICU/libSqliteIcu) viaSqliteConnection.LoadExtension(...)— confirmed present and documented onMicrosoft.Data.Sqliteacross 3.0–10.0 (Microsoft Learn). Gives ICU's full Unicode case-folding and collation support, but requires sourcing or building a compiled ICU extension binary for bothlinux/amd64andlinux/arm64and bundling it into the Docker image — an ongoing multi-arch maintenance cost.SqliteConnection.CreateCollation(...)orCreateFunction(...)(both confirmed present onMicrosoft.Data.Sqlite, no version gate relevant to Quotinator's 10.0.10). A C#-side case-fold (e.g.string.ToUpperInvariant()orCompareInfo-based comparison) run as a custom SQL function/collation, invoked from the relevantLIKE-adjacentWHEREclauses instead of relying on SQLite's nativeLIKE. No native binary, no multi-arch build cost — likely the lighter-weight option, at the cost of it only applying to queries explicitly rewritten to use it (not a blanket fix for everyLIKEin the codebase).Scope
LIKE-based query inSql.cs(/quotes/search,character/author/sourcefuzzy filters — see the "Entity-scoped filter-parameter convention" and/quotes/search's explicit LIKE exemption in CLAUDE.md) and apply the chosen fix.Out of scope
LOWER(column) = LOWER(@param)and unaffected by this issue either way.