1

从 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.

详细描述
有以下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 版上重现它。

4

1 回答 1

1

Postgres 没有正式对布尔语句进行快捷方式(例如,与 C 不同)

它确实说有时它可以决定走捷径(见文档),但它可能很容易决定在第二个表达式而不是第一个表达式上捷径。

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

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

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

IF (CASE WHEN TG_OP = 'INSERT' THEN TRUE ELSE OLD.name <> NEW.name END) THEN
于 2013-05-08T16:39:43.380 回答