70 lines
3.1 KiB
PL/PgSQL
70 lines
3.1 KiB
PL/PgSQL
-- 회의에 한 번이라도 쓰인 사용자 회의 문서 템플릿을 삭제할 수 있게 한다.
|
|
--
|
|
-- 문제: 20260821000025 가 meetings.template_id 를 ON DELETE RESTRICT 로 만들었고,
|
|
-- enforce_meeting_creation_identity_v1 은 RPC 로 생성된 회의의 template_id 변경을
|
|
-- 전부 거부했다. 그래서 커스텀 템플릿으로 회의를 한 번 만들면
|
|
-- delete_user_template_v1 / sync_delete_user_template_v1 이 23503(FK 위반)으로
|
|
-- 영구히 실패했고(데스크톱 아웃박스 삭제가 매 플러시마다 재시도), 회의에서
|
|
-- 템플릿 연결을 끊을 방법도 없었다.
|
|
--
|
|
-- 수정:
|
|
-- 1) FK 를 ON DELETE SET NULL 로 바꾼다 (meeting_documents.template_id 등 다른
|
|
-- user_templates 참조와 동일). 템플릿을 지우면 과거 회의에서 분리만 된다.
|
|
-- 2) RPC 생성 회의의 template_id 는 여전히 다른 템플릿으로 바꿀 수 없지만 NULL
|
|
-- 로의 분리는 허용한다. 원래 템플릿은 creation_request_hash 에 이미 기록되어
|
|
-- 있으므로 생성 무결성(idempotency 재시도 판정)은 그대로 유지된다. FK 의
|
|
-- SET NULL 참조 동작도 같은 BEFORE UPDATE OF template_id 트리거를 거치므로
|
|
-- 이 완화가 없으면 1) 만으로는 삭제가 여전히 실패한다.
|
|
|
|
ALTER TABLE public.meetings
|
|
DROP CONSTRAINT IF EXISTS meetings_template_id_fkey;
|
|
|
|
ALTER TABLE public.meetings
|
|
ADD CONSTRAINT meetings_template_id_fkey
|
|
FOREIGN KEY (template_id)
|
|
REFERENCES public.user_templates(id)
|
|
ON DELETE SET NULL
|
|
NOT VALID;
|
|
|
|
ALTER TABLE public.meetings
|
|
VALIDATE CONSTRAINT meetings_template_id_fkey;
|
|
|
|
CREATE OR REPLACE FUNCTION public.enforce_meeting_creation_identity_v1()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
SET search_path = pg_catalog, public, auth
|
|
AS $$
|
|
DECLARE
|
|
actor_id uuid := auth.uid();
|
|
rpc_actor text := current_setting('d3ro.meeting_creation_actor', true);
|
|
BEGIN
|
|
IF TG_OP = 'INSERT' THEN
|
|
IF NEW.creation_idempotency_key IS NOT NULL THEN
|
|
IF actor_id IS NULL OR rpc_actor IS DISTINCT FROM actor_id::text THEN
|
|
RAISE EXCEPTION 'meeting_creation_rpc_required' USING ERRCODE = '42501';
|
|
END IF;
|
|
ELSIF NEW.template_id IS NOT NULL
|
|
OR NEW.language IS NOT NULL
|
|
OR NEW.attendees <> '[]'::jsonb THEN
|
|
RAISE EXCEPTION 'meeting_creation_metadata_requires_rpc' USING ERRCODE = '42501';
|
|
END IF;
|
|
RETURN NEW;
|
|
END IF;
|
|
|
|
IF NEW.creation_idempotency_key IS DISTINCT FROM OLD.creation_idempotency_key
|
|
OR NEW.creation_request_hash IS DISTINCT FROM OLD.creation_request_hash THEN
|
|
RAISE EXCEPTION 'meeting_creation_identity_immutable' USING ERRCODE = '42501';
|
|
END IF;
|
|
-- 다른 템플릿으로의 재지정은 금지, NULL 로의 분리(템플릿 삭제 포함)는 허용.
|
|
IF OLD.creation_idempotency_key IS NOT NULL
|
|
AND NEW.template_id IS DISTINCT FROM OLD.template_id
|
|
AND NEW.template_id IS NOT NULL THEN
|
|
RAISE EXCEPTION 'meeting_creation_template_immutable' USING ERRCODE = '42501';
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
REVOKE ALL ON FUNCTION public.enforce_meeting_creation_identity_v1()
|
|
FROM PUBLIC, anon, authenticated;
|