36.3. Создание функций триггеров на языке C#
36.3. Создание функций триггеров на языке C #
Этот раздел описывает низкоуровневые детали интерфейса к триггерной функции. Эта информация нужна только при написании триггерных функций на языке C. Если вы используете язык более высокого уровня, то эти детали обрабатываются за вас. В большинстве случаев рекомендуется использовать процедурный язык перед написанием триггеров на C. Документация каждого процедурного языка объясняет, как написать триггер на этом языке.
Все функции триггеров должны использовать интерфейс менеджера функций “версии 1”.
Когда функция вызывается менеджером триггеров, ей не передаются никакие обычные аргументы, но ей передается указатель “context”, указывающий на структуру TriggerData
. Функции на языке C могут проверить, были ли они вызваны менеджером триггеров или нет, выполнив макрос:
CALLED_AS_TRIGGER(fcinfo)
который расширяется в:
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
Если это возвращает true, то безопасно привести fcinfo->context
к типу TriggerData *
и использовать указанную структуру TriggerData
. Функция не должна изменять структуру TriggerData
или любые данные, на которые она указывает.
Структура 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
Always
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)
Возвращает true, если триггер был запущен командой
INSERT
.TRIGGER_FIRED_BY_UPDATE(tg_event)
Возвращает true, если триггер был запущен командой
UPDATE
.TRIGGER_FIRED_BY_DELETE(tg_event)
Возвращает true, если триггер был запущен командой
DELETE
.TRIGGER_FIRED_BY_TRUNCATE(tg_event)
Возвращает true, если триггер был запущен командой
TRUNCATE
.
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
, и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))
.Для триггеров, отличных от
UPDATE
триггеров, это будетNULL
.
Для того чтобы позволить запросам, выполняемым через SPI, ссылаться на таблицы перехода, см. SPI_register_trigger_data.
Триггерная функция должна возвращать указатель на HeapTuple
или указатель на NULL
(не значение NULL SQL, то есть не устанавливайте isNull
в true). Будьте внимательны и возвращайте либо tg_trigtuple
, либо tg_newtuple
, в зависимости от ситуации, если вы не хотите изменять обрабатываемую строку.