从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版本上重现它。
答案 0 :(得分:1)
Postgres没有officially对布尔语句做短路(例如,与C不同)
它确实说它有时它可以决定做空(见docs)但它可能很容易决定在第二个表达而不是第一个表达式上做空。
它基本上考虑了在决定评估顺序之前每一侧的表达式有多复杂。然后,如果这是真的,它可以决定不打扰另一方。
在这种情况下,它似乎试图解释OLD
,而它仍然试图决定评估表达式的最佳顺序。
你应该可以通过使用CASE分割表达式来解决这个问题,例如。
IF (CASE WHEN TG_OP = 'INSERT' THEN TRUE ELSE OLD.name <> NEW.name END) THEN