Postgresql 中文操作指南

44.6. Trigger Functions in PL/Tcl #

触发器函数可以用 PL/Tcl 编写。PostgreSQL 要求作为触发器调用的函数必须被声明为不带参数且返回类型为 trigger 的函数。

触发器管理器的信息通过以下变量传递到函数主体:

  • $TG_name

    • CREATE TRIGGER 语句中的触发器名称。

  • $TG_relid

    • 导致触发器函数调用的表的对象 ID。

  • $TG_table_name

    • 导致触发器函数调用的表的名称。

  • $TG_table_schema

    • 导致触发器函数被调用的表的模式。

  • $TG_relatts

    • 一个 Tcl 表中的列名的列表,带有空列表元素作为前缀。因此,使用 Tcl 的 lsearch 命令在列表中查找列名会返回元素的编号,从第一列的 1 开始,这与通常在 PostgreSQL 中对列进行编号的方式相同。(在已删除列的位置也会出现空列表元素,以便属性编号对右侧的列正确。)

  • $TG_when

    • 字符串_BEFORE_、AFTER_或_INSTEAD OF,具体取决于触发事件类型。

  • $TG_level

    • 字符串_ROW_或_STATEMENT_,具体取决于触发事件类型。

  • $TG_op

    • 字符串_INSERT_、UPDATEDELETE_或_TRUNCATE,具体取决于触发事件类型。

  • $NEW

    • 一个关联数组,包含_INSERT_或_UPDATE_操作的新表行的值,或对于_DELETE_为空。数组由列名索引。值为null的列不会出现在数组中。对于语句级触发器未设置此项。

  • $OLD

    • 一个关联数组,包含_UPDATE_或_DELETE_操作的旧表行的值,或对于_INSERT_为空。数组由列名索引。值为null的列不会出现在数组中。对于语句级触发器未设置此项。

  • $args

    • 一个 Tcl 中给定的函数参数列表,在 CREATE TRIGGER 语句中。这些参数也可在函数体中作为 $1 …​ $_n_ 访问。

触发器函数的返回值可以是字符串_OK_或_SKIP_,或者一个列名/值对列表。如果返回值是_OK_,触发触发器的操作(INSERT/UPDATE/DELETE)将正常进行。SKIP_告诉触发器管理器静默抑制该行的操作。如果返回一个列表,它告诉PL/Tcl向触发器管理器返回一个修改后的行;修改后的行的内容由列表中的列名和值指定。列表中未提及的任何列均设为null。仅当返回修改后的行对于行级_BEFORE INSERT_或_UPDATE_触发器有意义,修改后的行将被插入而不是$NEW_中给出的行;或对于行级_INSTEAD OF_ INSERT_或_UPDATE_触发器,其中返回的行用作_INSERT RETURNING_或_UPDATE RETURNING_子句的源数据。在行级_BEFORE DELETE_或_INSTEAD OF _DELETE_触发器中,返回修改后的行的效果与返回_OK_相同,即操作进行。对于所有其他类型的触发器,都忽略触发器返回值。

Tip

结果列表可以用使用_array get_Tcl命令的修改后的元组的数组表示法生成。

这是一个小型的示例触发器函数,它强制表中的整型值跟踪对该行执行的更新次数。对于插入的新行,该值初始化为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 FUNCTION trigfunc_modcount('modcnt');

请注意,触发器函数本身不知道列名;这是从触发器参数中提供的。这允许触发器函数与不同的表重复使用。