Вопрос по triggers, postgresql, plpgsql, sql – Триггер Postgres после доступа к вставке NEW

22

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

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   ответ
41

прекрасное руководство:

36.1. Overview of Trigger Behavior
[...]
For a row-level trigger, the input data also includes the NEW row for INSERT and UPDATE triggers, and/or the OLD row for UPDATE and DELETE triggers. Statement-level triggers do not currently have any way to examine the individual row(s) modified by the statement.

И изПроцедуры запуска:

NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is NULL in statement-level triggers and for DELETE operations.

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

У вас есть триггер на уровне выписки:

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

Триггеры уровня оператора запускаются один раз для каждого оператора, и оператор может применяться к нескольким строкам, поэтому понятиеaffected row (который является то, что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.

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

A trigger function must return either NULL or a record/row value having exactly the structure of the table the trigger was fired for.
[...]
The return value of a row-level trigger fired AFTER or a statement-level trigger fired BEFORE or AFTER is always ignored; it might as well be null. However, any of these types of triggers might still abort the entire operation by raising an error.

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

Вы также добавилиRETURN NEW; до конца функции?
@kgrittn: Это было быnext ошибка, нет? Но да, я даже этого не видел, и стоит упомянуть, пока мы здесь.
да, я добавилRETURN заявление :) soupdiver

Похожие вопросы