-- Migration 017: leave_collective(uuid) RPC — Fase 10.1 (CU-H06). -- -- A user abandons one of their collectives. The content stays. -- -- If the user is the only admin and other members exist, the existing -- `promote_oldest_member_on_admin_leave` trigger fires on DELETE FROM -- public.users — but that trigger only runs on user-account delete, not -- on collective_members delete. So we replicate the auto-promote logic -- inline before removing the membership row. -- -- If the user is the sole member of the collective, leaving is disallowed: -- they must dissolve instead (CU-H08, migration 018). Errcode P0001 lets -- the UI distinguish this case from generic errors and link to the -- dissolve flow. CREATE OR REPLACE FUNCTION public.leave_collective(p_collective_id uuid) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = public, auth AS $$ DECLARE v_user uuid := auth.uid(); v_member_count int; v_is_admin boolean; v_other_admins int; BEGIN IF v_user IS NULL THEN RAISE EXCEPTION 'not authenticated' USING ERRCODE = '28000'; END IF; -- Guard: caller must actually be a member of the collective. IF NOT EXISTS ( SELECT 1 FROM public.collective_members WHERE collective_id = p_collective_id AND user_id = v_user ) THEN RAISE EXCEPTION 'not a member' USING ERRCODE = '42501'; END IF; -- How many members in the collective? SELECT count(*) INTO v_member_count FROM public.collective_members WHERE collective_id = p_collective_id; IF v_member_count = 1 THEN -- Sole member — must dissolve, not leave. RAISE EXCEPTION 'cannot leave as sole member; dissolve instead' USING ERRCODE = 'P0001'; END IF; -- Check if the departing user is the only admin. SELECT role = 'admin' INTO v_is_admin FROM public.collective_members WHERE collective_id = p_collective_id AND user_id = v_user; IF v_is_admin THEN SELECT count(*) INTO v_other_admins FROM public.collective_members WHERE collective_id = p_collective_id AND user_id <> v_user AND role = 'admin'; -- If sole admin, promote the oldest remaining (non-departing) member. IF v_other_admins = 0 THEN UPDATE public.collective_members SET role = 'admin' WHERE (collective_id, user_id) = ( SELECT collective_id, user_id FROM public.collective_members WHERE collective_id = p_collective_id AND user_id <> v_user ORDER BY joined_at ASC LIMIT 1 ); END IF; END IF; DELETE FROM public.collective_members WHERE collective_id = p_collective_id AND user_id = v_user; END; $$; REVOKE ALL ON FUNCTION public.leave_collective(uuid) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.leave_collective(uuid) TO authenticated;