You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL内置触发器suppress_redundant_updates_trigger疑似Bug问询

问题分析与解答

首先直接给出结论:这不是预期行为,你碰到的是PostgreSQL 12及更早版本中suppress_redundant_updates_trigger()的一个已知缺陷,根源在于触发器判断行变更的逻辑在表结构变更后的特殊处理上。

问题根源

suppress_redundant_updates_trigger()的核心逻辑是对比UPDATE前后行的物理tuple存储结构,而非单纯判断逻辑数据是否一致。当你执行ALTER TABLE ADD COLUMN后:

  • 现有行的磁盘存储tuple并不会立即重写,而是被标记为「缺少新列的存储信息」;
  • 当第一次对某行执行无变更的UPDATE时,PostgreSQL会自动重写该行的tuple,填充新列的默认值(这里是NULL);
  • 此时OLD tuple(未重写的旧结构)和NEW tuple(包含新列的完整结构)的物理存储存在差异,触发器就会误判为行发生了变更,不会抑制更新,进而触发后续的bz_am_i_touched()触发器。

对应你的测试场景拆解

  1. 初始状态:表结构稳定,所有行的tuple结构统一,无变更的UPDATE会被触发器正确识别并抑制,符合预期。
  2. 添加列后第一次执行UPDATE(id=1):该行tuple被强制重写,OLD和NEW的物理结构不同,触发器未抑制更新,导致后续触发器触发。
  3. 后续重复UPDATE(id=1):该行tuple已经是完整结构,OLD和NEW物理一致,恢复正常抑制。
  4. 第一次执行UPDATE(id=2):该行还未被重写,同样触发tuple重写逻辑,触发器再次误判。

解决方案

针对这个问题,有两种可行的处理方式:

  1. 手动重写所有行:在添加列后,执行一次全表的无变更UPDATE,强制所有行的tuple重写为新结构:
    UPDATE test SET id = id;
    
    之后suppress_redundant_updates_trigger()就能正常工作了。
  2. 升级PostgreSQL版本:这个问题在PostgreSQL 13及以上版本中已经被修复——新版本优化了触发器的判断逻辑,会忽略新增列默认值填充导致的物理tuple差异,只判断逻辑数据是否真的发生了变更。

内容的提问来源于stack exchange,提问作者Stephane Desnault

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 06:17:45