fix(backend): resolve folder_labels search_path on non-public (PG_SCHEMA) schemas (#9758)

* fix(backend): strip search_path=public from folder_labels migrations for non-public schema

The folder-labels migrations (20260610151334_folder_labels,
20260614075900_dedup_folder_labels) define `folder_labels(...)` with
`SET search_path = public` in their `CREATE FUNCTION` bodies. When Windmill
runs in a non-public schema (PG_SCHEMA), PostgreSQL validates the function
body against the `public` schema, where the `folder` table lacks the new
`labels` column, failing with `column "labels" does not exist`.

Add both migrations to OVERRIDDEN_MIGRATIONS, stripping the
`SET search_path = public` clause so the function inherits the current
search_path (which resolves the correct schema). Same regression and fix
pattern as PR #5400.

Fixes WIN-2093

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(backend): pin folder_labels search_path FROM CURRENT instead of stripping it

Keep the SECURITY DEFINER injection hardening while resolving the correct
schema on non-public (PG_SCHEMA) installs: FROM CURRENT snapshots the
migration connection's search_path at function creation time (public on
normal installs, the custom schema otherwise) instead of dropping the pin
and inheriting the caller's search_path at call time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(backend): repair migration to re-pin folder_labels search_path on applied instances

Instances that already applied the folder-labels migrations with the hardcoded
SET search_path = public have a folder_labels function pinned to public. On a
non-public (PG_SCHEMA) schema that reads the wrong folder table at runtime; the
OVERRIDDEN_MIGRATIONS fix only helps instances that have not applied them yet.

Add a CREATE OR REPLACE ... SET search_path FROM CURRENT migration that re-pins
the function to the migration connection's schema. No-op on public installs
(re-pins to public) and idempotent on already-correct ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-06-24 12:38:44 +02:00
committed by GitHub
parent 5e09c50171
commit f5828780fd
3 changed files with 31 additions and 0 deletions
@@ -0,0 +1,3 @@
-- No-op: this migration only re-pins the function's search_path. Reverting would
-- mean restoring the hardcoded `SET search_path = public`, which is the very bug
-- this repairs, so there is nothing to undo.
@@ -0,0 +1,22 @@
-- Repair instances that already applied the folder-labels migrations while the
-- function hardcoded `SET search_path = public`. On a non-public schema (PG_SCHEMA)
-- the function was pinned to `public`, so at runtime it read the wrong `folder`
-- table (or a stray public.folder) instead of the workspace's real one.
--
-- `FROM CURRENT` snapshots the migration connection's search_path (the actual
-- Windmill schema) into the function, keeping the SECURITY DEFINER injection
-- hardening. On public-schema installs this re-pins to `public`, i.e. a no-op.
-- Idempotent: redefining with the same body is harmless on already-correct installs.
CREATE OR REPLACE FUNCTION folder_labels(w_id text, item_path text) RETURNS text[]
LANGUAGE sql STABLE SECURITY DEFINER SET search_path FROM CURRENT AS $$
SELECT (
SELECT array_agg(l ORDER BY first_ord)
FROM (
SELECT u.l, min(u.ord) AS first_ord
FROM unnest(f.labels) WITH ORDINALITY AS u(l, ord)
GROUP BY u.l
) deduped
)
FROM folder f
WHERE f.workspace_id = w_id AND item_path LIKE 'f/%' AND f.name = split_part(item_path, '/', 2)
$$;
+6
View File
@@ -84,6 +84,12 @@ lazy_static::lazy_static! {
(20260228000000, include_str!(
"../../migrations/20260228000000_v2_job_completed_failure_index.up.sql"
).replace("CREATE INDEX", "CREATE INDEX CONCURRENTLY")),
(20260610151334, include_str!(
"../../migrations/20260610151334_folder_labels.up.sql"
).replace("SET search_path = public", "SET search_path FROM CURRENT").to_string()),
(20260614075900, include_str!(
"../../migrations/20260614075900_dedup_folder_labels.up.sql"
).replace("SET search_path = public", "SET search_path FROM CURRENT").to_string()),
].into_iter().collect();
}