31

我有一个非常简单的触发器:

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

为什么我会收到此错误?我查看了文档,似乎他们使用 new 的方式与我相同。

4

1 回答 1

49

来自精美手册

36.1。触发器行为概述
[...]
对于行级触发器,输入数据还包括NEW用于INSERTUPDATE触发器的行,和/或OLD用于UPDATEDELETE触发器的行。语句级触发器目前没有任何方法来检查语句修改的各个行。

并从触发程序

NEW
数据类型RECORD;为行级触发器中的INSERT/操作保存新数据库行的变量。UPDATE这个变量NULL在语句级触发器和DELETE操作中。

请注意它所说的关于行级触发器和语句级触发器的内容。

您有一个语句级触发器:

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

语句级触发器每条语句触发一次,并且一条语句可以应用于多行,因此受影响行的概念(即是什么NEWOLD是关于)根本不适用。

如果您想在触发器中使用NEW(or 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;.

于 2012-06-12T17:31:51.843 回答