36.3. Создание функций триггеров на языке C#

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