From 55d4fedab9069dc5b80349e16b8cd93a8de14a1e Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Sun, 31 May 2026 13:06:14 +0300 Subject: [PATCH] fix(db): create profile.account_id before is_account_member helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `is_account_member` is a `LANGUAGE sql` function, so Postgres parses and validates its body at CREATE time — references to `profiles.account_id` and `profiles.account_role` must already exist in the schema. The original ordering created the helper before the ALTER TABLE that added those columns, causing Supabase to abort with ERROR: column p.account_id does not exist (SQLSTATE 42703) on every fresh apply. Reordering: the profile column-add now runs immediately after the `accounts` / `account_invitations` tables and before the helper. No semantic change elsewhere. Co-Authored-By: Claude Opus 4.7 (1M context) --- supabase/migrations/017_account_sharing.sql | 32 ++++++++++++--------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/supabase/migrations/017_account_sharing.sql b/supabase/migrations/017_account_sharing.sql index 5a5e4e2..7f39743 100644 --- a/supabase/migrations/017_account_sharing.sql +++ b/supabase/migrations/017_account_sharing.sql @@ -106,6 +106,24 @@ CREATE INDEX IF NOT EXISTS idx_account_invitations_account_pending ALTER TABLE account_invitations ENABLE ROW LEVEL SECURITY; +-- ============================================================ +-- PROFILE EXTENSION +-- +-- account_role lives on profiles (not a separate memberships table) +-- because the design is one-account-per-user; this keeps reads cheap +-- (one row, already loaded by the auth hook). +-- +-- Added BEFORE the is_account_member helper below because LANGUAGE +-- sql functions resolve column references at CREATE time (unlike +-- plpgsql, which defers to call time). +-- ============================================================ +ALTER TABLE profiles + ADD COLUMN IF NOT EXISTS account_id UUID REFERENCES accounts(id) ON DELETE CASCADE, + ADD COLUMN IF NOT EXISTS account_role account_role_enum; + +CREATE INDEX IF NOT EXISTS idx_profiles_account_role + ON profiles(account_id, account_role); + -- ============================================================ -- MEMBERSHIP HELPER -- @@ -148,20 +166,6 @@ $$; ALTER FUNCTION is_account_member(UUID, account_role_enum) OWNER TO postgres; GRANT EXECUTE ON FUNCTION is_account_member(UUID, account_role_enum) TO authenticated, service_role; --- ============================================================ --- PROFILE EXTENSION --- --- account_role lives on profiles (not a separate memberships table) --- because the design is one-account-per-user; this keeps reads cheap --- (one row, already loaded by the auth hook). --- ============================================================ -ALTER TABLE profiles - ADD COLUMN IF NOT EXISTS account_id UUID REFERENCES accounts(id) ON DELETE CASCADE, - ADD COLUMN IF NOT EXISTS account_role account_role_enum; - -CREATE INDEX IF NOT EXISTS idx_profiles_account_role - ON profiles(account_id, account_role); - -- ============================================================ -- ADD account_id TO EVERY PARENT TENANT TABLE --