36.4. Полный пример триггера

Вот очень простой пример триггерной функции, написанной на C. (Примеры триггеров для процедурных языков могут быть найдены в документации на процедурные языки.)

Функция trigf сообщает количество строк в таблице ttest и пропускает операцию для строки при попытке вставить пустое значение в столбец x. (Таким образом, триггер действует как ограничение NOT NULL, но не прерывает транзакцию.)

Вначале определение таблицы:

CREATE TABLE ttest (
    x integer
);

Теперь исходный код триггерной функции:

#include "postgres.h"
#include "executor/spi.h"       /* это нужно для работы с SPI */
#include "commands/trigger.h"   /* ... с триггерами ... */
#include "utils/rel.h"          /* ... и с таблицами */

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

extern Datum trigf(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(trigf);

Datum
trigf(PG_FUNCTION_ARGS)
{
    TriggerData *trigdata = (TriggerData *) fcinfo->context;
    TupleDesc   tupdesc;
    HeapTuple   rettuple;
    char       *when;
    bool        checknull = false;
    bool        isnull;
    int         ret, i;

    /* Убедимся, что функция вызвана триггером */
    if (!CALLED_AS_TRIGGER(fcinfo))
        elog(ERROR, "trigf: not called by trigger manager");

    /* Строка, которую будем возвращать */
    if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
        rettuple = trigdata->tg_newtuple;
    else
        rettuple = trigdata->tg_trigtuple;

    /* Проверяем на пустые значения */
    if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
        && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
        checknull = true;

    if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
        when = "before";
    else
        when = "after ";

    tupdesc = trigdata->tg_relation->rd_att;

    /* Подключаемся к менеджеру SPI */
    if ((ret = SPI_connect()) < 0)
        elog(ERROR, "trigf (сработал %s): SPI_connect вернула %d", when, ret);

    /* Получаем число строк в таблице */
    ret = SPI_exec("SELECT count(*) FROM ttest", 0);

    if (ret < 0)
        elog(ERROR, "trigf (сработал %s): SPI_exec вернула %d", when, ret);

    /* count(*) возвращает int8, требуется преобразование */
    i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
                                    SPI_tuptable->tupdesc,
                                    1,
                                    &isnull));

    elog (INFO, "trigf (сработал %s): в таблице ttest %d строк", when, i);

    SPI_finish();

    if (checknull)
    {
        SPI_getbinval(rettuple, tupdesc, 1, &isnull);
        if (isnull)
            rettuple = NULL;
    }

    return PointerGetDatum(rettuple);
}

После компиляции исходного кода (см. Подраздел 35.9.6) объявляем функцию и триггеры:

CREATE FUNCTION trigf() RETURNS trigger
    AS 'filename'
    LANGUAGE C;

CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
    FOR EACH ROW EXECUTE PROCEDURE trigf();

CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
    FOR EACH ROW EXECUTE PROCEDURE trigf();

Теперь можно проверить работу триггера:

=> INSERT INTO ttest VALUES (NULL);

INFO:  trigf (сработал before): в таблице ttest 0 строк
INSERT 0 0

-- Вставка записи пропущена (NULL значение), поэтому AFTER триггер не сработал

=> SELECT * FROM ttest;
 x
---
(0 rows)

=> INSERT INTO ttest VALUES (1);
INFO:  trigf (сработал before): в таблице ttest 0 строк
INFO:  trigf (сработал after ): в таблице ttest 1 строк
                                                ^^^^^^^
                                   вспомним, что говорили о видимости
INSERT 167793 1
vac=> SELECT * FROM ttest;
 x
---
 1
(1 row)

=> INSERT INTO ttest SELECT x * 2 FROM ttest;
INFO:  trigf (сработал before): в таблице ttest 1 строк
INFO:  trigf (сработал after ): в таблице ttest 2 строк
                                                ^^^^^^^
                                   вспомним, что говорили о видимости
INSERT 167794 1
=> SELECT * FROM ttest;
 x
---
 1
 2
(2 rows)

=> UPDATE ttest SET x = NULL WHERE x = 2;
INFO:  trigf (сработал before): в таблице ttest 2 строк
UPDATE 0
=> UPDATE ttest SET x = 4 WHERE x = 2;
INFO:  trigf (сработал before): в таблице ttest 2 строк
INFO:  trigf (сработал after ): в таблице ttest 2 строк
UPDATE 1
vac=> SELECT * FROM ttest;
 x
---
 1
 4
(2 rows)

=> DELETE FROM ttest;
INFO:  trigf (сработал before): в таблице ttest 2 строк
INFO:  trigf (сработал before): в таблице ttest 1 строк
INFO:  trigf (сработал after ): в таблице ttest 0 строк
INFO:  trigf (сработал after ): в таблице ttest 0 строк
                                                ^^^^^^^
                                   вспомним, что говорили о видимости
DELETE 2
=> SELECT * FROM ttest;
 x
---
(0 rows)

Более сложные примеры можно найти в src/test/regress/regress.c и в spi.

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy