-- Unicode lowercasing in the built-in analyzer (to_ftsdoc(text)/to_ftsquery(text)): -- a token with non-ASCII bytes is lowercased per Unicode code point, so accented -- letters match case-insensitively ('CAFÉ' matches 'café'). The document side -- (built at index time) and the query side must fold identically, so every query -- below is run under a forced SEQ SCAN and a forced INDEX/BITMAP SCAN and must -- return the same rows. (This is simple lowercasing, not case folding: 'ß' stays -- 'ß', so 'STRASSE' does not match 'Straße'.) SET client_min_messages = warning; CREATE TABLE ufold (id int, body text); INSERT INTO ufold VALUES (1, 'café society'), (2, 'CAFÉ RESTAURANT'), (3, 'Le Café'), (4, 'naïve approach'), (5, 'NAÏVE idea'), (6, 'MÜNCHEN city'), (7, 'münchen bier'), (8, 'RÉSUMÉ text'), (9, 'Straße Berlin'), (10, 'plain ascii only'); -- ASCII filler so the planner prefers the index once seq scan is disabled INSERT INTO ufold SELECT g, 'filler token number ' || g FROM generate_series(100, 3000) g; CREATE INDEX ufold_fts ON ufold USING fts (to_ftsdoc(body)); ANALYZE ufold; -- The query side folds too: 'CAFÉ' -> 'café', 'MÜNCHEN' -> 'münchen' SELECT to_ftsquery('CAFÉ')::text AS q_cafe, to_ftsquery('MÜNCHEN')::text AS q_munchen, to_ftsquery('STRASSE')::text AS q_strasse; -- ===== forced SEQ SCAN (@@@ evaluated on the heap, doc + query folded live) ===== SET enable_indexscan = off; SET enable_bitmapscan = off; SET enable_seqscan = on; EXPLAIN (COSTS OFF) SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('CAFÉ'); SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('CAFÉ') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('münchen') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('naïve') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('résumé') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('STRASSE') ORDER BY id; -- ===== forced INDEX/BITMAP SCAN (@@@ answered from the built terms) ===== SET enable_seqscan = off; SET enable_indexscan = on; SET enable_bitmapscan = on; EXPLAIN (COSTS OFF) SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('CAFÉ'); SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('CAFÉ') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('münchen') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('naïve') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('résumé') ORDER BY id; SELECT id FROM ufold WHERE to_ftsdoc(body) @@@ to_ftsquery('STRASSE') ORDER BY id; RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; DROP TABLE ufold;