fix(catalog): wrap series-name DISTINCT in subquery so ORDER BY LOWER(...) works

Postgres rejects ORDER BY expressions that aren't in the SELECT list
when SELECT DISTINCT is used (\"ORDER BY expressions must appear in
select list\"). The original listDistinctAudiobookSeriesWithSource
emitted SELECT DISTINCT BTRIM(s.series_name) with ORDER BY
LOWER(BTRIM(s.series_name)) and failed with that error, which surfaced
as a 500 on every /api/v1/catalog/filters request that hit the
audiobook-series facet.

Push the DISTINCT into an inline subquery aliased as \"names\"; the
outer SELECT then orders by LOWER(name) without running afoul of the
distinct-vs-order rule. Same trimming + case-folding behaviour for
sort, same trimmed literal returned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
RXWatcher
2026-05-27 19:09:23 +02:00
co-authored by Claude Opus 4.7
parent 3357c49d72
commit 3e35da7f0c
+16 -9
View File
@@ -762,9 +762,14 @@ func listDistinctPeopleByKindWithSource(
// listDistinctAudiobookSeriesWithSource returns distinct series_name values
// from audiobook_series joined onto the scoped result set. Names are trimmed
// and case-folded in the ORDER BY so the picker doesn't show duplicates that
// differ only by whitespace or casing — the literal series_name is still
// and case-folded for sort so the picker doesn't show duplicates that differ
// only by whitespace or casing — the literal trimmed series_name is still
// returned so existing rules continue to match.
//
// The DISTINCT happens in an inline subquery (rather than directly on the
// outer SELECT) so the outer ORDER BY can apply LOWER(...) without violating
// Postgres' "ORDER BY expressions must appear in select list" rule for
// SELECT DISTINCT.
func listDistinctAudiobookSeriesWithSource(
ctx context.Context,
pool *pgxpool.Pool,
@@ -777,13 +782,15 @@ func listDistinctAudiobookSeriesWithSource(
return []string{}, nil
}
query := fmt.Sprintf(`
SELECT DISTINCT BTRIM(s.series_name) AS series_name
FROM %s
JOIN audiobook_series s ON s.content_id = mi.content_id
%s
AND s.series_name IS NOT NULL
AND BTRIM(s.series_name) <> ''
ORDER BY LOWER(BTRIM(s.series_name)) ASC
SELECT name FROM (
SELECT DISTINCT BTRIM(s.series_name) AS name
FROM %s
JOIN audiobook_series s ON s.content_id = mi.content_id
%s
AND s.series_name IS NOT NULL
AND BTRIM(s.series_name) <> ''
) names
ORDER BY LOWER(name) ASC
`, fromClause, browseFilterPrefix(whereClause))
return queryDistinctStrings(ctx, pool, query, args)
}