37.3. Триггерные функции на языке C #
В этом разделе описываются низкоуровневые детали интерфейса для триггерных функций. Эта информация необходима только при разработке триггерных функций на языке C. При использовании языка более высокого уровня эти детали обрабатываются не видны. В большинстве случаев стоит рассмотреть возможность использования процедурного языка, прежде чем начать разрабатывать триггеры на C. В документации по каждому процедурному языку объясняется, как создавать триггеры на этом языке.
Триггерные функции должны использовать интерфейс функций «версии 1».
Когда функция вызывается диспетчером триггеров, ей не передаются обычные аргументы, но передаётся указатель «context», ссылающийся на структуру TriggerData
. Функции на C могут проверить, вызваны ли они диспетчером триггеров или нет, выполнив макрос:
CALLED_AS_TRIGGER(fcinfo)
который разворачивается в:
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
Если возвращается истина, то 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)
Возвращает истину, если триггер сработал до операции.
TRIGGER_FIRED_AFTER(tg_event)
Возвращает истину, если триггер сработал после операции.
TRIGGER_FIRED_INSTEAD(tg_event)
Возвращает истину, если триггер сработал вместо операции.
TRIGGER_FIRED_FOR_ROW(tg_event)
Возвращает истину, если триггер сработал на уровне строки.
TRIGGER_FIRED_FOR_STATEMENT(tg_event)
Возвращает истину, если триггер сработал на уровне оператора.
TRIGGER_FIRED_BY_INSERT(tg_event)
Возвращает истину, если триггер сработал для операции
INSERT
.TRIGGER_FIRED_BY_UPDATE(tg_event)
Возвращает истину, если триггер сработал для операции
UPDATE
.TRIGGER_FIRED_BY_DELETE(tg_event)
Возвращает истину, если триггер сработал для операции
DELETE
.TRIGGER_FIRED_BY_TRUNCATE(tg_event)
Возвращает истину, если триггер сработал для операции
TRUNCATE
.
tg_relation
Указатель на структуру, описывающую таблицу, для которой сработал триггер. Подробнее об этой структуре в
utils/rel.h
. Самое интересное здесь этоtg_relation->rd_att
(дескриптор записей таблицы) иtg_relation->rd_rel->relname
(имя таблицы; имеет типNameData
, а неchar*
; используйтеSPI_getrelname(tg_relation)
, чтобы получить типchar*
если потребуется копия имени).tg_trigtuple
Указатель на строку, для которой сработал триггер. Это строка, которая вставляется, обновляется или удаляется. При срабатывании триггера для
INSERT
илиDELETE
это значение нужно вернуть из функции, только если не планируется изменять строку (в случаеINSERT
) или пропускать операцию для этой строки.tg_newtuple
Для триггера на
UPDATE
это указатель на новую версию строки либоNULL
, если триггер наINSERT
илиDELETE
. Это значение нужно вернуть из функции в случае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
, или указательNULL
, если переходное отношениеOLD TABLE
отсутствует.tg_newtable
Указатель на структуру типа
Tuplestorestate
, содержащую ноль или несколько строк в формате, определяемом содержимымtg_relation
, или указательNULL
, если переходное отношениеNEW TABLE
отсутствует.tg_updatedcols
Для триггеров
UPDATE
— битовая карта, в которой отмечается, какие столбцы изменила команда, вызвавшая срабатывание триггера. Используя её, универсальные триггерные функции могут оптимизировать свои действия, не обращая внимания на столбцы, которые не были изменены.Определить, вошёл ли в битовую карту столбец с атрибутом под номером
attnum
(считая с 1), можно так:bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
.Для всех остальных триггеров содержит
NULL
.
Чтобы обращаться к переходным таблицам в запросах, выполняемых через SPI, используйте SPI_register_trigger_data.
Триггерная функция должна возвращать указатель HeapTuple
или указатель NULL
(но не SQL значение null
, то есть не нужно устанавливать isNull
в истину). Не забудьте, что если не планируете менять обрабатываемую триггером строку, то нужно вернуть либо tg_trigtuple
, либо tg_newtuple
.