42.6. PL/Tcl 中的触发器过程

触发器过程也可以用 PL/Tcl 编写。PostgreSQL要求能作为触发器被调用的过程必须被声明为一个没有参数并且返回类型为trigger的函数。

来自于触发器管理器的信息通过下列变量被传递给过程体:

$TG_name

CREATE TRIGGER语句中触发器的名字。

$TG_relid

导致触发器过程被调用的表的对象 ID。

$TG_table_name

导致触发器过程被调用的表的名字。

$TG_table_schema

导致触发器过程被调用的表所在的模式。

$TG_relatts

表列名的 Tcl 列表,前面放上一个空列表元素。因此用Tcllsearch命令在该列表中查找一个列名返回的元素编号会从 1 开始(对于第一列),这和PostgreSQL中的自定义编号是同样的方式(空列表元数也出现在被删除的列的位置上,这样其右边的列的属性编号才是正确的)。

$TG_when

可以为BEFOREAFTER或者INSTEAD OF,具体的选择取决于触发器事件的类型。

$TG_level

可以为ROW或者STATEMENT,取决于触发器事件的类型。

$TG_op

可以为INSERTUPDATEDELETE或者TRUNCATE,取决于触发器事件的类型。

$NEW

对于INSERT或者UPDATE动作是一个包含着新表行值的关联数组,对于DELETE为空。该数组以列名为索引。为空的列不会出现在数组中。对于语句级触发器这个变量不会被设置。

$OLD

对于UPDATE或者DELETE动作是一个包含着新表行值的关联数组,对于INSERT为空。该数组以列名为索引。为空的列不会出现在数组中。对于语句级触发器这个变量不会被设置。

$args

CREATE TRIGGER语句中对过程给出的参数的 Tcl 列表。在过程体中也可以用$1 ... $n来访问这些参数。

触发器过程的返回值可以是OK或者SKIP,还可以是一个由array get Tcl 命令返回的列表。如果返回值是OK,引发触发器的操作(INSERT/UPDATE/DELETE)将正常继续。SKIP告诉触发器管理器禁止对这一行的操作。如果返回的是列表,它告诉 PL/Tcl 返回一个被修改行给触发器管理器。这会对行级的BEFORE INSERT或者UPDATE触发器有意义,对这些触发器被修改行将被插入而不是插入$NEW中给定的行。列表对于行级INSTEAD OF INSERT或者UPDATE触发器也有意义, 在其中返回的行被用来支持INSERT RETURNING以及UPDATE RETURNING命令。对其他类型的触发器来说返回值会被忽略。

这里有一个触发器过程的例子,它用一个表中的整数值来跟踪在行上被执行的更新数。对于被插入的新行,该值被初始化为 0 并且之后在每一次更新操作时被加一。

CREATE FUNCTION trigfunc_modcount() RETURNS trigger AS $$
    switch $TG_op {
        INSERT {
            set NEW($1) 0
        }
        UPDATE {
            set NEW($1) $OLD($1)
            incr NEW($1)
        }
        default {
            return OK
        }
    }
    return [array get NEW]
$$ LANGUAGE pltcl;

CREATE TABLE mytab (num integer, description text, modcnt integer);

CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
    FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt');

注意触发器过程本身不知道列名,列名由触发器参数提供。这让触发器过程可以被重用于不同的表。