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