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

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