2013-05-08 141 views
1

我遇到了非常奇怪的错误。从PostgreSQL服务器版本9迁移到<strong>8.4</strong> PostgreSQL触发器引发错误55000

简短说明
如果插入之前,给定表的触发器为每一行或更新和条件语句一个使用(如果其他)TG_OP值检查和OLD对象,以下

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

详细描述
doinng INSERT当错误引起3210有以下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版本上重现它。

+0

对于INSERT语句,OLD不存在。 (你已经知道了;其他人访问这里可能不会)。为了测试,可能需要重写'TG_OP ='INSERT''和'OLD.name <> NEW.name'单独的条件语句。即,将OLD.name <> NEW.name'移动到ELSE子句或ELSEIF子句。 – 2013-05-08 15:44:50

+0

@ MikeSherrill'Catcall',你是对的,但这是关键。把它分成单独的条件就可以完成这项工作,但重点是将它集于一身。在真实的例子中,我是这样做的临时解决方案,但我不得不复制 - 粘贴整块代码... – Wiktor 2013-05-08 15:51:59

+0

如果工作,我的下一个测试可能是添加parens以保证正确的优先级:IF(TG_OP ='INSERT ')OR(OLD.name <> NEW.name)THEN'。话虽如此,我并不知道8.4中的优先级错误,但我以前一直不知道这样的东西。 – 2013-05-08 15:56:10

回答

1

Postgres没有officially做布尔语句走捷径(不像C为例)

它说出来,有时它可以决定捷径(见docs),但它可能只是轻松地决定要走捷径在第二个表达式而不是第一个表达式上。

它基本上看在决定评估顺序之前,每一面的表达式有多复杂。那么如果这是真的,它可以决定不打扰对方。

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

你应该能够通过使用CASE来拆分表达式,例如。

​​
+0

非常有趣,乐于助人!谢谢,加里:) – Wiktor 2013-05-08 20:16:26