postgresql中insert规则必须用do instead才能拦截原始插入,do also仅追加动作;去重需用exists子查询配合do instead nothing;视图规则名强制为_return,非视图规则命名自由但建议带前缀;规则不校验权限、不触发触发器,仅查询重写阶段文本替换。

INSERT规则必须用INSTEAD才能拦截原始插入
直接写DO ALSO无法阻止原INSERT执行,只会额外追加动作。想真正“拦截”——比如去重、审计、路由到其他表——必须显式声明INSTEAD,否则PostgreSQL仍会照常插入原始行。
常见错误是漏写INSTEAD,结果发现数据重复写入:原始INSERT进了表,规则又执行了一次INSERT或NOTHING,但原始行已存在。
-
DO INSTEAD:原始INSERT被完全替换,只执行规则里定义的动作 -
DO ALSO:原始INSERT照常执行,再额外执行规则动作(适合审计日志) - 不写
INSTEAD或ALSO:默认等价于DO ALSO
去重场景下WHERE条件必须用EXISTS而非直接比较NEW
想实现类似INSERT IGNORE效果,不能写WHERE NEW.id = (SELECT id FROM table)——这会报错,因为子查询不能引用NEW以外的表(除非用EXISTS)。
正确写法是把判断逻辑包在EXISTS里,让PostgreSQL允许该上下文访问目标表:
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
CREATE RULE r_insert_ignore AS ON INSERT TO system_lock WHERE EXISTS (SELECT 1 FROM system_lock WHERE id = NEW.id) DO INSTEAD NOTHING;
- 用
EXISTS绕过“不能在WHERE中直接查本表”的限制 -
NEW.id必须在子查询内引用,不能提出来做等值判断 - 这条规则生效后,重复
INSERT INTO system_lock (id) VALUES (123)将静默失败,不报错也不写入
规则名不是随便起的,视图必须叫_RETURN
如果规则用于模拟视图(即ON SELECT),规则名必须是_RETURN,否则PostgreSQL拒绝创建:
ERROR: view rule for "v_test2" must be named "_RETURN"
而普通表上的INSERT规则命名自由,但要注意:同一张表上多个INSERT规则按名称字典序触发,顺序不可控。实际生产中,一张表只配一条INSTEAD规则更稳妥,避免隐式叠加。
- 视图规则名硬性要求
_RETURN,大小写敏感 - 非视图规则名可自定义,但建议带前缀如
r_orders_insert_audit便于识别 - 多条规则共存时,不保证执行顺序——别依赖“先A后B”,合并逻辑到单条规则里
规则系统不校验权限,也不触发触发器
规则在查询重写阶段工作,早于权限检查和触发器执行。这意味着:
- 即使用户没有对目标表的
INSERT权限,只要规则DO INSTEAD指向另一张他有权限的表,操作仍可能成功 - 原表上的
BEFORE INSERT触发器不会被调用——规则已经把原始INSERT替换了 - 规则动作里的SQL语句,会以当前用户身份执行,不自动提升权限
真正要防越权写入,得靠后续的行级安全策略(RLS)或触发器校验,规则本身只是文本替换,不带业务语义判断能力。










