Триггер Postgres после вставки, доступ к NEW

У меня довольно простой триггер:

CREATE OR REPLACE FUNCTION f_log_datei()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id);
END; $$ LANGUAGE 'plpgsql';

CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();

Мои журналы таблицы следующие:

CREATE TABLE logs(
    id int PRIMARY KEY DEFAULT NEXTVAL('logs_id_seq'),
    zeit timestamp DEFAULT now(),
    aktion char(6),
    tabelle varchar(32),
    alt varchar(256),
    neu varchar(256),
    benutzer_id int references benutzer(id)
);

После ввода чего-то в dateien я получаю следующую ошибку:

ERROR:  record "new" is not assigned yet
DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT:  SQL statement "INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id)"
PL/pgSQL function "f_log_datei" line 3 at SQL statement

Почему я получил эту ошибку? Я просмотрел документацию, и кажется, что они используют новые так же, как и я.

Ответы

Ответ 1

Из точное руководство:

36,1. Обзор поведения триггеров
[...]
Для триггера уровня уровня входные данные также включают строку NEW для триггеров INSERT и UPDATE и/или строку OLD для триггеров UPDATE и DELETE. Триггеры уровня уровня не имеют способа изучить отдельные строки, измененные оператором.

И из Триггерные процедуры:

NEW
Тип данных RECORD; переменная, содержащая новую строку базы данных для операций INSERT/UPDATE в триггерах уровня. Эта переменная NULL в триггерах уровня инструкций и для операций DELETE.

Обратите внимание на то, что он говорит о триггерах уровня и триггерах уровня инструкций.

У вас есть триггер уровня инструкции:

...
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();

Триггеры уровня выполнения запускаются один раз для каждого оператора, и оператор может применяться к нескольким строкам, поэтому понятие затронутой строки (что означает NEW и OLD) просто не применяется.

Если вы хотите использовать NEW (или OLD) в триггере, то вы хотите, чтобы триггер выполнялся для каждой затронутой строки, а это означает, что вам нужен триггер уровня:

CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH ROW
EXECUTE PROCEDURE f_log_datei();

Я только что изменил FOR EACH STATEMENT на FOR EACH ROW.


Ваш триггер должен также возвращать что-то:

Функция триггера должна возвращать либо NULL, либо значение записи/строки, имеющее ровно структуру таблицы, на которой был запущен триггер.
[...]
Возвращаемое значение триггера уровня уровня, запущенного AFTER или триггер уровня инструкции, запущенный BEFORE или AFTER, всегда игнорируется; он также может быть нулевым. Однако любой из этих типов триггеров может все же прервать всю операцию, вызвав ошибку.

Итак, вы должны RETURN NEW; или RETURN NULL; в вашем триггере. У вас есть триггер AFTER, поэтому не имеет значения, какой RETURN вы используете, но я бы пошел с RETURN NEW;.