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