# Return a list of the indices of all occurrences (including overlapping occurrences) create or replace function string_positions(haystack, needle) as ( WITH RECURSIVE etc AS ( SELECT position(needle IN haystack) AS start_pos, 0 as s UNION ALL SELECT position(needle IN substr(haystack, start_pos + 1)) + start_pos AS start_pos, start_pos as s FROM etc WHERE start_pos != s ) SELECT coalesce(array_agg(start_pos order by start_pos),[]::BIGINT[]) FROM etc WHERE start_pos > 0 and s != start_pos );