인적오류는 사람이 하기에 발생하는 문제이다.
은총탄은 없다.
이 포스트에서는 PGSQL을 기반으로 설명합니다.
원문 https://www.postgresql.org/docs/current/sql-createtrigger.html
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
ON table_name
[ FROM referenced_table_name ]
[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( condition ) ]
EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )
where event can be one of:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
CREATE [OR REPLACE]문은 schema를 만들거나 대체하는 것을 의미한다.CONSTRAINT가 붙어있다면, 이 Trigger는 무조건 실행 후 발동되는 Trigger이며, 트랜잭션의 끝에 실행된다. 또한, ROW TRIGGER 만 가능하다.{ BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }에서는 발동 타이밍과 이벤트를 정할 수 있다. (이벤트는, INSERT/UPDATE/DELETE/TRUNCATE중 하나이다.)INSTEAD OF [INSERT...]라면 INSERT대신 이 Trigger가 발생한다. ON table_name : 테이블 이름을 지정할 수 있다.[ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]: 변경시 초기화 조건에 대한 제약.[ FOR [ EACH ] { ROW | STATEMENT } ] : ROW 단위의 Trigger인지 혹은 STATEMENT단위의 Trigger인지를 의미한다. 이것은 ROW 업데이트 이후 트리거를 발동하는지, 혹은 한꺼번에 업데이트 이후 발동하는지의 차이가 있다.[ WHEN ( condition ) ] : 실행시의 조건을 의미한다.EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments ) : 실행되는 트리거 함수/프로시저를 의미한다.https://www.postgresql.org/docs/current/plpgsql-trigger.html
NEW : 새롭게 변경된 값이 적용된 행 OLD : 대체되는 행의 기존값을 가진 행.CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when they must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE FUNCTION emp_stamp();
emp에 INSERT/UPDATE 이전에 행해지는 구문으로서,EACH ROW에 대해 가해지는 사전처리 함수입니다.empname과 salary가 NULL이라면 RAISE EXCEPTION하고, 또한 salary < 0 일때도 마찬가지의 처리를 합니다.INSERT 혹은 UPDATE 합니다.함수에 정의하는것을 중요하게 생각하자!Trigger는
backend/commands/trigger.c에 정의되어 있다.
CreateTrigger에 정의되어 있다.CreateTrigger의 return값은 ObjectAddress이다.CreateTriggerFiringOn를 호출한다.ObjectAddress
CreateTrigger(
CreateTrigStmt *stmt,
const char *queryString,
Oid relOid,
Oid refRelOid,
Oid constraintOid,
Oid indexOid,
Oid funcoid,
Oid parentTriggerOid,
Node *whenClause,
bool isInternal,
bool in_partition);
/* ----------------------
* Create TRIGGER Statement
* ----------------------
*/
typedef struct CreateTrigStmt
{
NodeTag type;
bool replace; /* replace trigger if already exists */
bool isconstraint; /* This is a constraint trigger */
char *trigname; /* TRIGGER's name */
RangeVar *relation; /* relation trigger is on */
List *funcname; /* qual. name of function to call */
List *args; /* list of String or NIL */
bool row; /* ROW/STATEMENT */
/* timing uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
int16 timing; /* BEFORE, AFTER, or INSTEAD */
/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
int16 events; /* "OR" of INSERT/UPDATE/DELETE/TRUNCATE */
List *columns; /* column names, or NIL for all columns */
Node *whenClause; /* qual expression, or NULL if none */
/* explicitly named transition data */
List *transitionRels; /* TriggerTransition nodes, or NIL if none */
/* The remaining fields are only used for constraint triggers */
bool deferrable; /* [NOT] DEFERRABLE */
bool initdeferred; /* INITIALLY {DEFERRED|IMMEDIATE} */
RangeVar *constrrel; /* opposite relation, if RI trigger */
} CreateTrigStmt;
typedef unsigned int Oid;
CreateTriggerFiringOn는 실제로 Trigger를 만드는 역할을 한다.HeapTuple ExecCallTriggerFunc을 통해 이루어진다.static HeapTuple
ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
FmgrInfo *finfo,
Instrumentation *instr,
MemoryContext per_tuple_context)
를 호출하고, HeapTuple이라는 객체를 반환한다.
LOCAL_FCINFO에서 호출을 요구하는 함수를 초기화하고, InitFunctionCallInfoData에서 Trigger 정보와 함께 묶은 다음, 묶인 함수(정확히는 함수의 포인터)를 FunctionCallInvoke로 호출한다. HeapTuple type의 데이터로 return 하여 trigger를 마친다.