이벤트 트리거 동작 개요
이벤트 트리거 동작 개요 (Overview of Event Trigger Behavior)
앞에서 이벤트 트리거가 DDL까지 캐치할 수 있다고 했죠. 그런데 "정확히 언제" 발화되는지는 이벤트 종류에 따라 달라져요. 이 페이지에서는 PostgreSQL이 지원하는 다섯 가지 이벤트가 각각 어떤 시점에 어떤 방식으로 발생하는지, 그리고 실패했을 때 트랜잭션이 어떻게 되는지를 하나씩 살펴볼게요.
이벤트 트리거는 어떤 이벤트로 발화되나요?
이벤트 트리거는 자신이 정의된 데이터베이스 안에서 연관된 이벤트가 발생할 때마다 발화돼요. 현재 지원되는 이벤트는 다섯 가지입니다.
loginddl_command_startddl_command_endtable_rewritesql_drop
이 외의 이벤트가 앞으로 추가될 여지도 있어요.
login 이벤트
login 이벤트는 인증된 사용자가 시스템에 로그인할 때 발생해요. 이 이벤트용 트리거 프로시저에 버그가 있으면 로그인 자체가 막힐 수 있으니 조심해야 해요. 이런 상황이 벌어졌을 때는 커넥션 문자열이나 설정 파일에서 event_triggers를 false로 바꿔 우회할 수 있고, 아니면 단일 사용자(single-user) 모드로 시스템을 재시작할 수도 있어요(이 모드에서는 이벤트 트리거가 비활성화되거든요).
login 이벤트는 스탠바이 서버에서도 발화돼요. 서버가 접근 불가능해지는 사고를 막으려면, 스탠바이에서 돌고 있을 때 트리거가 데이터베이스에 아무것도 쓰지 않도록 해야 해요. 또 login 트리거 안에서 오래 걸리는 쿼리를 실행하는 것도 피하는 게 좋아요. 예를 들어 psql에서 커넥션을 취소해도 진행 중인 login 트리거는 취소되지 않는다는 점도 알아 두세요.
사용 예는 공식 문서의 Section 38.5(데이터베이스 로그인 이벤트 트리거 예제)에서 볼 수 있어요.
ddl_command_start 이벤트
ddl_command_start 이벤트는 DDL 명령이 실행되기 직전에 발생해요. 여기서 말하는 DDL 명령은 이런 것들입니다.
CREATEALTERDROPCOMMENTGRANTIMPORT FOREIGN SCHEMAREINDEXREFRESH MATERIALIZED VIEWREVOKESECURITY LABEL
SELECT INTO도 CREATE TABLE AS와 동등하기 때문에, 실행 직전에 ddl_command_start가 발생해요.
다만 몇 가지 예외가 있어요. 공유 객체(shared objects)를 대상으로 하는 DDL에서는 이 이벤트가 발생하지 않아요. 구체적으로 데이터베이스, 역할(role 정의·멤버십), 테이블스페이스, 파라미터 권한, ALTER SYSTEM이 해당돼요. 그리고 이벤트 트리거 자체를 대상으로 하는 명령에서도 발생하지 않아요.
트리거가 발화되기 전에 대상 객체가 존재하는지 여부는 검사하지 않아요. 이 점도 헷갈리기 쉬우니 기억해 두면 좋아요.
ddl_command_end 이벤트
ddl_command_end 이벤트는 ddl_command_start와 동일한 명령 집합이 실행된 직후에 발생해요. 실제로 어떤 DDL 작업이 일어났는지 더 자세히 알고 싶다면, ddl_command_end 트리거 코드 안에서 집합 반환 함수 pg_event_trigger_ddl_commands()를 사용하면 돼요. 트리거는 작업이 일어난 뒤(하지만 트랜잭션이 커밋되기 전)에 발화되므로, 시스템 카탈로그를 이미 변경된 상태로 읽을 수 있어요.
sql_drop 이벤트
sql_drop 이벤트는 데이터베이스 객체를 삭제하는 모든 작업에서, ddl_command_end 이벤트 트리거 직전에 발생해요. 눈에 띄는 DROP 명령뿐 아니라 일부 ALTER 명령도 sql_drop 이벤트를 유발할 수 있다는 점이 포인트예요.
삭제된 객체 목록을 확인하려면 sql_drop 트리거 코드 안에서 집합 반환 함수 pg_event_trigger_dropped_objects()를 사용해요. 이 트리거는 객체가 시스템 카탈로그에서 삭제된 뒤 실행되므로, 그 객체를 다시 조회하는 것은 불가능해요.
table_rewrite 이벤트
table_rewrite 이벤트는 ALTER TABLE과 ALTER TYPE의 일부 동작에 의해 테이블이 재작성(rewrite)되기 직전에 발생해요. CLUSTER나 VACUUM처럼 테이블을 재작성할 수 있는 다른 제어문에서는 이 이벤트가 발생하지 않아요. 재작성된 테이블의 OID는 pg_event_trigger_table_rewrite_oid(), 재작성 사유는 pg_event_trigger_table_rewrite_reason()로 알아낼 수 있어요.
중단된 트랜잭션에서의 이벤트 트리거
이벤트 트리거(다른 함수와 마찬가지로)는 중단된(aborted) 트랜잭션 안에서는 실행될 수 없어요. 그래서:
- DDL 명령이 오류로 실패하면 연관된
ddl_command_end트리거는 실행되지 않아요. - 반대로
ddl_command_start트리거가 오류로 실패하면 이후 이벤트 트리거가 발화하지 않고, 명령 자체의 실행도 시도되지 않아요. ddl_command_end트리거가 오류로 실패하면 DDL 문장의 효과가 롤백돼요. 트랜잭션이 중단되는 다른 모든 경우와 마찬가지예요.
이벤트 트리거 만들기
이벤트 트리거는 CREATE EVENT TRIGGER 명령으로 만들 수 있어요. 만들기 전에 먼저 특별한 반환 타입인 event_trigger를 갖는 함수를 정의해야 해요. 이 함수는 값을 반환할 필요도 없고 반환해선도 안 돼요. 반환 타입은 단지 "이 함수가 이벤트 트리거로 호출된다"는 신호일 뿐입니다.
같은 이벤트에 이벤트 트리거가 여러 개 정의되어 있으면, 트리거 이름의 알파벳 순서대로 발화돼요.
트리거 정의에는 WHEN 조건을 붙일 수도 있어요. 예를 들어 ddl_command_start 트리거를 사용자가 가로채고 싶은 특정 명령에 대해서만 발화되게 만들 수 있어요. 이런 트리거는 사용자가 수행할 수 있는 DDL 작업의 범위를 제한하는 데 흔히 쓰여요.