【问题标题】:PostgreSQL trigger raises error 55000PostgreSQL 触发器引发错误 55000
【发布时间】: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 &lt;&gt; NEW.name 单独的条件语句。也就是说,将 OLD.name &lt;&gt; NEW.name 移动到 ELSE 子句或 ELSEIF 子句。
  • @MikeSherrill'Catcall',你是对的,但这就是重点。把它放在单独的条件句中就可以了,但重点是把它放在一个整体上。在真实的例子中,我这样做是作为临时解决方案,但我不得不复制粘贴整个代码块......
  • 如果可行,我的下一个测试可能是添加括号以保证正确的优先级:IF (TG_OP = 'INSERT') OR (OLD.name &lt;&gt; NEW.name) THEN。话虽如此,我不知道 8.4 中的优先级错误,但我以前不知道这样的事情。
  • @MikeSherrill'Catcall' - 试过了 :))
  • 用于 TG_OP 删除和更新的其他 ELSEIF?

标签: postgresql triggers


【解决方案1】:

Postgres 不会 officially 对布尔语句执行快捷方式(例如,与 C 不同)

它确实说过有时它可以决定使用快捷方式(请参阅docs),但它可能很容易决定使用第二个表达式而不是第一个表达式。

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

在这种情况下,它看起来像是在尝试解释 OLD,同时仍在尝试确定评估表达式的最佳顺序。

您应该可以通过使用 CASE 来拆分表达式来解决此问题,例如。

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

【讨论】:

  • 非常有趣且乐于助人!谢谢你,加里 :)
猜你喜欢
  • 2011-06-19
  • 2017-09-13
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多