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Всегда
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, в зависимости от ситуации, если вы не хотите изменять обрабатываемую строку.