【发布时间】:2013-05-08 15:31:14
【问题描述】:
从 PostgreSQL 服务器版本 9 迁移到 8.4 后,我遇到了非常奇怪的错误。
简短说明:
如果给定表的每一行在插入或更新之前都有触发器,并且在条件语句(if-else)TG_OP值检查和OLD对象中使用,则出现以下错误执行 INSERT 时引发:
ERROR: record "old" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
详细说明:
有以下数据库结构:
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 版本上重现它。
【问题讨论】:
-
OLD 对于 INSERT 语句不存在。 (你已经知道了;其他访问这里的人可能不会。)为了测试,可能值得重写以使
TG_OP = 'INSERT'和OLD.name <> NEW.name单独的条件语句。也就是说,将OLD.name <> NEW.name移动到 ELSE 子句或 ELSEIF 子句。 -
@MikeSherrill'Catcall',你是对的,但这就是重点。把它放在单独的条件句中就可以了,但重点是把它放在一个整体上。在真实的例子中,我这样做是作为临时解决方案,但我不得不复制粘贴整个代码块......
-
如果可行,我的下一个测试可能是添加括号以保证正确的优先级:
IF (TG_OP = 'INSERT') OR (OLD.name <> NEW.name) THEN。话虽如此,我不知道 8.4 中的优先级错误,但我以前不知道这样的事情。 -
@MikeSherrill'Catcall' - 试过了 :))
-
用于 TG_OP 删除和更新的其他 ELSEIF?
标签: postgresql triggers