23.9. 触发器过程 #
PL/pgSQL可以用来定义触发器过程。触发器过程用CREATE FUNCTION命令创建,是一个没有参数、返回类型为OPAQUE的函数。注意即使函数期望接收CREATE TRIGGER中指定的参数,它也必须被声明为没有参数——触发器参数通过TG_ARGV传递,如下所述。
当一个PL/pgSQL函数作为触发器被调用时,顶层块中会自动创建几个特殊变量。它们是:
NEW数据类型
RECORD;在行级触发器中保存 INSERT/UPDATE 操作的新数据库行的变量。OLD数据类型
RECORD;在行级触发器中保存 UPDATE/DELETE 操作的旧数据库行的变量。TG_NAME数据类型
name;包含实际被触发的触发器名字的变量。TG_WHEN数据类型
text;依据触发器定义而是BEFORE或AFTER之一的字符串。TG_LEVEL数据类型
text;依据触发器定义而是ROW或STATEMENT之一的字符串。TG_OP数据类型
text;INSERT、UPDATE或DELETE之一的字符串,指出触发器是为哪个操作而触发。TG_RELID数据类型
oid;导致触发器调用的表的对象 ID。TG_RELNAME数据类型
name;导致触发器调用的表的名字。TG_NARGS数据类型
integer;CREATE TRIGGER语句中给触发器过程的参数个数。TG_ARGV[]text类型的数组;来自CREATE TRIGGER语句的参数。索引从 0 开始计数并且可以用表达式给出。无效的索引(< 0 或 >= tg_nargs)得到 NULL 值。
触发器函数必须返回 NULL,或者返回一个与触发该触发器的表的结构完全相同的记录/行值。BEFORE 触发器可以返回 NULL 来通知触发器管理器跳过这一行操作的其余部分(即不再触发后续触发器,而且这一行的 INSERT/UPDATE/DELETE 不会发生)。如果返回的是非 NULL 值,操作就会以那个行值继续进行。注意返回一个与 NEW 的原始值不同的行值会改变将被插入或更新的行。可以直接在 NEW 中替换单个值然后返回它,也可以构造一个完整的新记录/行来返回。
AFTER 触发器的返回值会被忽略;它总是返回 NULL 值也无妨。但 AFTER 触发器仍然可以通过引发错误来中止操作。
例 23.1. 一个PL/pgSQL触发器过程示例
这个示例触发器保证每当表中有行被插入或更新时,当前的用户名和时间都会被盖章到该行中。而且它还保证给出了员工的名字并且薪水是一个正值。
CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
BEGIN
-- Check that empname and salary are given
IF NEW.empname ISNULL THEN
RAISE EXCEPTION ''empname cannot be NULL value'';
END IF;
IF NEW.salary ISNULL THEN
RAISE EXCEPTION ''% cannot have NULL salary'', NEW.empname;
END IF;
-- Who works for us when she must pay for?
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 := ''now'';
NEW.last_user := current_user;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();