本部分介绍触发器功能的接口的底层细节。仅在使用 C 编写触发器功能时才需要了解此信息。如果你使用的是更高级的语言,则会为你处理这些细节。大多数情况下,你应考虑在使用 C 编写触发器前使用过程语言。每个过程语言的文档会说明如何在该语言中编写触发器。
触发器功能必须使用 “版本 1” 函数管理器接口。
触发器管理器调用了一个函数时,不会传递任何普通的参数,但会传递一个指向 TriggerData
结构的 “context” 指针。C 函数可以通过执行宏
CALLED_AS_TRIGGER(fcinfo)
来检查它们是否是由触发器管理器调用的
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
如果这返回 true,则可将 fcinfo->context
转换为类型 TriggerData *
,并使用所指向的 TriggerData
结构。该函数 不能 更改 TriggerData
结构或它指向的任何数据。
struct TriggerData
在 commands/trigger.h
中定义
typedef struct TriggerData { NodeTag type; TriggerEvent tg_event; Relation tg_relation; HeapTuple tg_trigtuple; HeapTuple tg_newtuple; Trigger *tg_trigger; TupleTableSlot *tg_trigslot; TupleTableSlot *tg_newslot; Tuplestorestate *tg_oldtable; Tuplestorestate *tg_newtable; const Bitmapset *tg_updatedcols; } TriggerData;
其中,成员被定义如下
type
始终是 T_TriggerData
。
tg_event
描述函数被调用的事件。你可以使用以下宏来检查 tg_event
TRIGGER_FIRED_BEFORE(tg_event)
如果在操作前触发了触发器,则返回 true。
TRIGGER_FIRED_AFTER(tg_event)
如果在操作后触发了触发器,则返回 true。
TRIGGER_FIRED_INSTEAD(tg_event)
如果触发器在操作替代时触发,则返回 true。
TRIGGER_FIRED_FOR_ROW(tg_event)
如果行级事件触发了触发器,则返回 true。
TRIGGER_FIRED_FOR_STATEMENT(tg_event)
如果语句级事件触发了触发器,则返回 true。
TRIGGER_FIRED_BY_INSERT(tg_event)
如果 INSERT
命令触发了该触发器,则返回 true。
TRIGGER_FIRED_BY_UPDATE(tg_event)
如果 UPDATE
命令触发了该触发器,则返回 true。
TRIGGER_FIRED_BY_DELETE(tg_event)
如果 DELETE
命令触发了该触发器,则返回 true。
TRIGGER_FIRED_BY_TRUNCATE(tg_event)
如果 TRUNCATE
命令触发了该触发器,则返回 true。
tg_relation
一个指向结构的指针,描述触发器触发的关系。查看 utils/rel.h
以获得有关此结构的详细信息。最有趣的事情是 tg_relation->rd_att
(关系元组的描述符)和 tg_relation->rd_rel->relname
(关系名称;类型不是 char*
而是 NameData
;如果需要名称的副本,请使用 SPI_getrelname(tg_relation)
来获取 char*
)。
tg_trigtuple
触发器触发所在行的指针。即待插入、更新或删除的行。如果此触发器为 INSERT
或 DELETE
触发,那么如果你不想使用另一行替换行(在 INSERT
的情况下),或者跳过操作,则应将此从函数中返回。对于外键表上的触发器,此处系统列的值不作说明。
tg_newtuple
如果触发器为 UPDATE
触发,则指向新版本行的指针;如果为 INSERT
或 DELETE
触发,则为 NULL
。如果事件为 UPDATE
触发且你不想用另一行替换此行或跳过操作,则应将此从函数中返回。对于外键表上的触发器,此处系统列的值不作说明。
tg_trigger
指向类型为 Trigger
的结构体的指针,该结构体在 utils/reltrigger.h
typedef struct Trigger { Oid tgoid; char *tgname; Oid tgfoid; int16 tgtype; char tgenabled; bool tgisinternal; bool tgisclone; Oid tgconstrrelid; Oid tgconstrindid; Oid tgconstraint; bool tgdeferrable; bool tginitdeferred; int16 tgnargs; int16 tgnattr; int16 *tgattr; char **tgargs; char *tgqual; char *tgoldtable; char *tgnewtable; } Trigger;
中定义,其中 tgname
是触发器的名称,tgnargs
是 tgargs
中的参数数,tgargs
是 CREATE TRIGGER
语句中指定的参数的指针数组。其他成员仅供内部使用。
tg_trigslot
包含 tg_trigtuple
的插槽,或者如果不存在此类元组,则为 NULL
指针。
tg_newslot
包含 tg_newtuple
的插槽,或者如果不存在此类元组,则为 NULL
指针。
tg_oldtable
指向类型为 Tuplestorestate
的结构体的指针,该结构体包含按 tg_relation
指定的格式的零行或多行,或者如果不存在 OLD TABLE
转换关系,则为 NULL
指针。
tg_newtable
指向类型为 Tuplestorestate
的结构体的指针,该结构体包含按 tg_relation
指定的格式的零行或多行,或者如果不存在 NEW TABLE
转换关系,则为 NULL
指针。
tg_updatedcols
对于 UPDATE
触发器,bitmap 设置,用于指示由触发命令更新的列。通用触发器函数可使用此功能优化操作,避免处理未更改的列。
例如,要确定属性编号为 attnum
(1 为基) 的列是否是此 bitmap 集合的成员,请调用 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
。
对于 UPDATE
触发器以外的触发器,此值将为 NULL
。
如希望通过 SPI 发出的查询能引用转换表,请参见 SPI_register_trigger_data。
触发器函数必须返回 HeapTuple
指针或 NULL
指针(不是 SQL null 值,即不要将 isNull
设置为 true)。注意返回 tg_trigtuple
或 tg_newtuple
(具体视情况而定),如果您不希望修改正在操作的行。