PostgreSQL中的错误抑制[redundant_updates_trigger]?

问题描述

我正在Postgresql中处理一组触发器,我想我偶然发现了内置函数/触发器suppress_redundant_updates_trigger()上的一个错误。在我的配置(笔记本电脑上的Postgresql 12)上完全可以复制。

首先,我建立了一个表,其中包含两个“每行之前”触发器:

CREATE TABLE test (id int,val text);
INSERT INTO test VALUES (1,'one'),(2,'two');

CREATE OR REPLACE FUNCTION am_i_touched() RETURNS trigger LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
    RAISE NOTICE 'Yes,I am touched!';
    RETURN NEW;
END;
$BODY$;

CREATE TRIGGER az_test_suppress_redundant_update
    BEFORE UPDATE ON public.test
    FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();

-- Make sure trigger name is after the prevIoUs one 
-- in alphabetical order as it drives execution order
CREATE TRIGGER bz_am_I_touched
    BEFORE UPDATE ON public.test 
    FOR EACH ROW EXECUTE PROCEDURE am_i_touched();

然后我运行UPDATE test SET id = 1 WHERE id = 1。正如预期的那样,由于该行保持不变,因此第一个触发器抑制了更新,并且从未触发bz_am_i_touched()。到目前为止一切顺利。

但是我跑了:

ALTER TABLE test ADD COLUMN newcol int

现在,我再次运行UPDATE test SET id = 1 WHERE id = 1 ...这一次,更新未被抑制,bz_am_i_touched()被触发! PGAdmin(v4)报告更新了一条记录,而不是像以前那样为零!

这是一次性事件。 UPDATE test SET id = 1 WHERE id = 1可以按预期进行进一步的工作...但是后来我尝试了UPDATE test SET id = 2 WHERE id = 2 ...,然后再次出现了这种奇怪的行为-更新未受到抑制。

这是预期的行为吗?我不明白UPDATE test SET id = 1 WHERE id = 1怎么可能导致更新不被禁止

解决方法

新元组和旧元组之间表示newcol NULL值的方式不同。因此,它们被认为是不相同的,因此不会抑制更新。

将元组与memcmp进行了总计比较,因此即使用户看不见的字节之间的差异也很明显。它不会遍历每个字段,就哪些差异在语义上有意义做出了与类型有关的单独决定。出于速度和简便性的考虑,这似乎是有意的。我怀疑它会被视为错误。