PostgreSQL触发器引发错误55000

时间:2013-05-08 15:31:15

标签: postgresql triggers

从PostgreSQL服务器版本9迁移到 8.4 后,我遇到了非常奇怪的错误。

简短说明
如果在插入或更新之前每行在给定表上有触发器,并且在条件语句(if-else)中使用TG_OP值检查和 OLD 对象,则会出现以下错误在doinng INSERT

时加注
ERROR:  record "old" is not assigned yet
DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.

详细说明
有以下DB结构:

CREATE TABLE table1
(
  id serial NOT NULL,
  name character varying(256),
  CONSTRAINT table1_pkey PRIMARY KEY (id)
)
WITH (OIDS=FALSE);

CREATE OR REPLACE FUNCTION exemplary_function()
RETURNS trigger AS
$BODY$    BEGIN
IF TG_OP = 'INSERT' OR OLD.name <> NEW.name THEN
    NEW.name = 'someName';
    END IF;

RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;

CREATE TRIGGER trigger1
  BEFORE INSERT OR UPDATE
  ON table1
FOR EACH ROW EXECUTE PROCEDURE exemplary_function();

并跟随触发错误的SQL查询:

INSERT INTO table1 (name) VALUES ('other name')

似乎解析器没有在TG_OP = 'INSERT'条件下停止(它应该,因为它是真的)但是检查另一个并且触发错误。 有趣的是,我只能在8.4版本上重现它。

1 个答案:

答案 0 :(得分:1)

Postgres没有officially对布尔语句做短路(例如,与C不同)

它确实说它有时它可以决定做空(见docs)但它可能很容易决定在第二个表达而不是第一个表达式上做空。

它基本上考虑了在决定评估顺序之前每一侧的表达式有多复杂。然后,如果这是真的,它可以决定不打扰另一方。

在这种情况下,它似乎试图解释OLD,而它仍然试图决定评估表达式的最佳顺序。

你应该可以通过使用CASE分割表达式来解决这个问题,例如。

IF (CASE WHEN TG_OP = 'INSERT' THEN TRUE ELSE OLD.name <> NEW.name END) THEN