19.9. 触发器过程

PL/pgSQL 可以用于定义触发器过程. 一个触发器过程是用 CREATE FUNCTION 命令 创建的,创建的形式是一个不接受参数并且返回 trigger 类型的函数.请注意该函数即使在 CREATE TRIGGER 声明里 声明为准备接受参数,它也必需声明为无参数 --- 触发器的参数是通过 TG_ARGV 传递的,下面有描述.

在一个 PL/pgSQL 函数当做触发器调用的时候, 系统会在顶层的声明段里自动创建几个特殊变量.有如下这些∶

NEW

数据类型是 RECORD; 该变量为 INSERT/UPDATE 操作时保存行(ROW)一级的触发器新的数据库行. 在 STATEMENT 级别的触发器里,这个变量是NULL。

OLD

据类型是 RECORD; 该变量为 INSERT/UPDATE 操作时保存行(ROW)一级的触发器新的数据库行. 在 STATEMENT 级别的触发器里,这个变量是NULL。

TG_NAME

数据类型是 name;该变量包含实际触发的触发器名. fired.

TG_WHEN

数据类型是 text;是一个由触发器定义决定的字符串, 要么是 BEFORE 要么是 AFTER

TG_LEVEL

据类型是 text;是一个由触发器定义决定的字符串, 要么是 ROW 要么是 STATEMENT

TG_OP

数据类型是 text;是一个说明触发触发器的操作的字符串, 可以是 INSERTUPDATE 或者 DELETE

TG_RELID

数据类型是 oid;是导致触发器调用的表的对象标识(OID).

TG_RELNAME

数据类型是 name;是激活触发器调用的表的名称.

TG_NARGS

数据类型是 INTEGER; 是在CREATE TRIGGER 语句里面赋予触发器过程的参数的个数.

TG_ARGV[]

数据类型是 text 的数组;是 CREATE TRIGGER语句里的参数. 下标从 0 开始记数,并且可以由一个表达式来表示.非法下标( < 0 or >= tg_nargs)导致一个 NULL 值的返回.

一个触发器函数必须返回 NULL 或者是 一个与导致触发器运行的表的记录/行完全一样的结构的数据. BEFORE 或者 AFTER STATEMENT 级别的触发器, 或者一个 AFTER ROW 级别的触发器的返回值被忽略; 它们也可以返回 NULL 来忽略返回值。不过,任何这种类型的触发器仍然可以 通过抛出一个错误来退出整个触发器操作。

BEFORE 类的触发器可以返回一个 NULL,发送一个信号给触发器管理器 告诉它忽略对实际行剩下的操作(也就是说,随后的触发器将不再执行,并且不会 对该行产生 INSERT/UPDATE/DELETE动作).如果返回了一个非 NULL 的行, 那么将继续对该行数值进行处理. 请注意,返回一个和原来的 NEW 不同的行数值将修改那个将插入或更新的行. 我们可能用一个值直接代替 NEW 里的某个数值并且 返回之,或者我们也可以构建一个完全新的记录/行再返回.

为 BEFORE 触发的 ROW 级别的触发器可以返回 NULL 告诉触发器管理器 忽略对此行的所有其他操作(也就是说,随后的触发器将不会执行, 并且不会对该行产生 INSERT/UPDATE/DELETE动作)。 如果返回了一个非 NULL 的行,那么将继续对该行数值进行处理。 请注意,返回一个和原来的 NEW 不同的行数值将修改那个将插入或更新的行. 我们可能用一个值直接代替 NEW 里的某个数值并且 返回之,或者我们也可以构建一个完全新的记录/行再返回.

Example 19-1. 一个PL/pgSQL触发器过程实例

下面的例子触发器的作用是:任何时候表中插入或更新了行, 当前的用户名和时间都记录入行中. 并且它保证给出了雇员名称并且薪水是一个正数.

CREATE TABLE emp (
    empname text,
    salary integer,
    last_date timestamp,
    last_user text
);

CREATE FUNCTION emp_stamp () RETURNS TRIGGER AS '
    BEGIN
        -- 检查是否给出了 empname 和 salary
        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;

        -- 我们必须付帐给谁?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname;
        END IF;

        -- 记住何时何人的薪水被修改了
        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();