From c4ebbd990c5c441df37c7bee7b0c2bc0d9f22501 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 25 Mar 2026 20:51:21 -0400 Subject: [PATCH] fix: resolve tags autocomplete SQL error with CROSS JOIN LATERAL - Fix SearchTagsValues query to use CROSS JOIN LATERAL instead of unnest() in WHERE clause - PostgreSQL error: "set-returning functions are not allowed in WHERE" - Change from direct unnest() calls to a proper lateral join pattern - References: tag.value instead of repeated unnest(mi.tags_search) calls - Regenerate Go code with sqlc generate This fixes the tags autocomplete functionality which was failing with SQLSTATE 0A000 error. The CROSS JOIN LATERAL approach properly expands the tags array before filtering, allowing set-returning functions to work correctly in the query. Relates to TestTagsFilter tags autocomplete test --- internal/database/queries.sql.go | 13 +++++++------ internal/database/queries/queries.sql | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/internal/database/queries.sql.go b/internal/database/queries.sql.go index 47ac5fc..cb598fa 100644 --- a/internal/database/queries.sql.go +++ b/internal/database/queries.sql.go @@ -7561,18 +7561,19 @@ func (q *Queries) SearchSeriesValues(ctx context.Context, arg SearchSeriesValues const SearchTagsValues = `-- name: SearchTagsValues :many SELECT - unnest(mi.tags_search)::TEXT as value, + tag.value::TEXT as value, COUNT(*) as count, - word_similarity($1, unnest(mi.tags_search))::float8 as score + word_similarity($1, tag.value)::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $2 +CROSS JOIN LATERAL unnest(mi.tags_search) AS tag WHERE COALESCE(lv.is_visible, true) = true AND ($3::uuid IS NULL OR mi.library_id = $3::uuid) - AND unnest(mi.tags_search) IS NOT NULL - AND word_similarity($1, unnest(mi.tags_search)) > 0.3 -GROUP BY unnest(mi.tags_search), word_similarity($1, unnest(mi.tags_search))::float8 -ORDER BY word_similarity($1, unnest(mi.tags_search))::float8 DESC, COUNT DESC + AND tag.value IS NOT NULL + AND word_similarity($1, tag.value) > 0.3 +GROUP BY tag.value, word_similarity($1, tag.value)::float8 +ORDER BY word_similarity($1, tag.value)::float8 DESC, COUNT DESC LIMIT $5 OFFSET $4 ` diff --git a/internal/database/queries/queries.sql b/internal/database/queries/queries.sql index 4761cf9..9614f4b 100644 --- a/internal/database/queries/queries.sql +++ b/internal/database/queries/queries.sql @@ -565,18 +565,19 @@ LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); -- name: SearchTagsValues :many SELECT - unnest(mi.tags_search)::TEXT as value, + tag.value::TEXT as value, COUNT(*) as count, - word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8 as score + word_similarity(sqlc.narg('search_query'), tag.value)::float8 as score FROM media_items mi JOIN libraries l ON mi.library_id = l.id LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id') +CROSS JOIN LATERAL unnest(mi.tags_search) AS tag WHERE COALESCE(lv.is_visible, true) = true AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid) - AND unnest(mi.tags_search) IS NOT NULL - AND word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search)) > 0.3 -GROUP BY unnest(mi.tags_search), word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8 -ORDER BY word_similarity(sqlc.narg('search_query'), unnest(mi.tags_search))::float8 DESC, COUNT DESC + AND tag.value IS NOT NULL + AND word_similarity(sqlc.narg('search_query'), tag.value) > 0.3 +GROUP BY tag.value, word_similarity(sqlc.narg('search_query'), tag.value)::float8 +ORDER BY word_similarity(sqlc.narg('search_query'), tag.value)::float8 DESC, COUNT DESC LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset'); -- name: SearchSeriesValues :many