CREATE FUNCTION public.enforce_chat_direct_channel_invariant() RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog, public AS $$ DECLARE canonical_key text; DECLARE member_count integer; BEGIN IF OLD.kind = 'direct' AND (NEW.kind IS DISTINCT FROM OLD.kind OR NEW.direct_key IS DISTINCT FROM OLD.direct_key OR NEW.workspace_id IS DISTINCT FROM OLD.workspace_id) THEN RAISE EXCEPTION 'direct channel identity is immutable' USING ERRCODE = '23514', CONSTRAINT = 'chat_direct_channel_identity_immutable'; END IF; IF OLD.kind <> 'direct' AND NEW.kind = 'direct' THEN IF NEW.workspace_id IS DISTINCT FROM OLD.workspace_id OR NEW.name IS NOT NULL OR NEW.direct_key IS NULL THEN RAISE EXCEPTION 'invalid direct channel transition' USING ERRCODE = '23514', CONSTRAINT = 'chat_direct_channel_transition_valid'; END IF; SELECT md5(string_agg(m.user_id::text, ':' ORDER BY m.user_id::text)), count(*) INTO canonical_key, member_count FROM public.chat_channel_members m WHERE m.workspace_id = NEW.workspace_id AND m.channel_id = NEW.id; IF member_count < 2 OR canonical_key IS DISTINCT FROM NEW.direct_key OR NOT EXISTS ( SELECT 1 FROM public.chat_channel_members m WHERE m.workspace_id = NEW.workspace_id AND m.channel_id = NEW.id AND m.user_id = NEW.owner_user_id ) THEN RAISE EXCEPTION 'direct channel participants do not match canonical key' USING ERRCODE = '23514', CONSTRAINT = 'chat_direct_channel_participants_valid'; END IF; END IF; RETURN NEW; END; $$; --> statement-breakpoint REVOKE ALL ON FUNCTION public.enforce_chat_direct_channel_invariant() FROM PUBLIC; --> statement-breakpoint CREATE TRIGGER chat_direct_channel_invariant BEFORE UPDATE ON public.chat_channels FOR EACH ROW EXECUTE FUNCTION public.enforce_chat_direct_channel_invariant(); --> statement-breakpoint CREATE FUNCTION public.enforce_chat_direct_membership_immutability() RETURNS trigger LANGUAGE plpgsql SET search_path = pg_catalog, public AS $$ BEGIN IF EXISTS (SELECT 1 FROM public.chat_channels c WHERE c.workspace_id = COALESCE(OLD.workspace_id, NEW.workspace_id) AND c.id = COALESCE(OLD.channel_id, NEW.channel_id) AND c.kind = 'direct') OR (TG_OP = 'UPDATE' AND EXISTS (SELECT 1 FROM public.chat_channels c WHERE c.workspace_id = NEW.workspace_id AND c.id = NEW.channel_id AND c.kind = 'direct')) THEN RAISE EXCEPTION 'direct channel membership is immutable' USING ERRCODE = '23514', CONSTRAINT = 'chat_direct_membership_immutable'; END IF; RETURN COALESCE(NEW, OLD); END; $$; --> statement-breakpoint REVOKE ALL ON FUNCTION public.enforce_chat_direct_membership_immutability() FROM PUBLIC; --> statement-breakpoint CREATE TRIGGER chat_direct_membership_immutability BEFORE INSERT OR UPDATE OR DELETE ON public.chat_channel_members FOR EACH ROW EXECUTE FUNCTION public.enforce_chat_direct_membership_immutability();