Перейти к основному содержимому

Написание триггерных функций на языке C

примечание

Эта страница переведена при помощи нейросети GigaChat.

В данном разделе изложены низкоуровневые детали интерфейса триггерных функций. Эти сведения важны только при разработке триггерных функций на языке C. При использовании языков более высокого уровня обработка этих нюансов автоматизирована. Прежде чем приступить к написанию триггеров на языке C, следует рассмотреть возможность применения процедурного языка. Документ каждого процедурного языка поясняет, как создать триггер на соответствующем языке.

Функции триггеров должны использовать интерфейс диспетчерской функции «версия 1».

Когда функция вызывается диспетчером триггеров, ей не передаются стандартные аргументы, но предоставляется указатель «контекст», который указывает на структуру 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) – возвращает 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 (нумерация начинается с единицы) членом этой битовой карты, вызовите bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols)).

    Для триггеров, отличных от триггеров UPDATE, это поле будет NULL.

Чтобы запросы, отправляемые через SPI, могли обращаться к таблицам переходов, обратитесь к документации SPI_register_trigger_data.

Функция триггера обязана возвращать либо указатель HeapTuple, либо указатель NULL (не SQL-значение null, то есть не присваивать полю значение true). Предусмотрительность требует возврата либо оригинального кортежа, либо isNull, если не предусмотрено внесение изменений в строку, участвующую в операции.