正确导出普通表触发器需JOIN pg_class并过滤relkind='r',补全CREATE TRIGGER前缀及分号,手动导出依赖函数,并确保执行用户具备相应权限。

pg_get_triggerdef()必须JOIN pg_class过滤普通表
直接查pg_trigger会把视图、分区子表、物化视图上的触发器也拉进来,而pg_get_triggerdef()对这些对象可能返回空或报错。真正要导出的只是绑定在普通用户表(relkind = 'r')上的触发器。
正确写法是显式关联pg_class并过滤:SELECT pg_get_triggerdef(oid) FROM pg_trigger t JOIN pg_class c ON t.tgrelid = c.oid WHERE c.relkind = 'r';
漏掉JOIN pg_class或写成c.relkind IN ('r', 'p')(含分区表)都可能导致结果不可用——某些分区子表 OID 在pg_get_triggerdef()里不被识别。
导出结果缺CREATE TRIGGER前缀,得手动拼
pg_get_triggerdef()返回的是不含CREATE TRIGGER关键字的定义片段,比如只返回BEFORE INSERT ON users FOR EACH ROW EXECUTE FUNCTION log_user_insert()。直接执行会报错ERROR: syntax error at or near "BEFORE"。
必须自己补全开头和换行:SELECT 'CREATE TRIGGER ' || quote_ident(t.tgname) || E'\n' || pg_get_triggerdef(t.oid) || ';' FROM pg_trigger t JOIN pg_class c ON t.tgrelid = c.oid WHERE c.relkind = 'r';
-
quote_ident()防止触发器名含特殊字符(如连字符、大小写)导致语法错误 -
E'\n'确保换行可读,避免所有内容挤在一行 - 末尾加
;是psql执行必需的语句分隔符
触发器依赖的函数不会自动导出
如果触发器里调用了自定义函数(比如EXECUTE FUNCTION audit_log()),pg_get_triggerdef()只返回调用语句,不包含audit_log()本身的定义。恢复时若目标库没这个函数,CREATE TRIGGER会失败。
得额外跑一次函数导出:SELECT pg_get_functiondef(oid) FROM pg_proc WHERE proname IN (SELECT distinct tgfname FROM pg_trigger);
注意:tgfname字段在较新版本中已弃用,实际应从pg_get_triggerdef()结果里正则提取函数名,或改用pg_depend反向查依赖关系——这点容易被忽略,一导出就漏依赖。
非超级用户执行时权限受限
普通用户执行上述查询,可能看不到其他 schema 下的触发器,或pg_get_triggerdef()返回 NULL。这不是语法问题,而是权限控制:需要对触发器所在表有SELECT权限,且对pg_trigger等系统表有访问权(通常只有pg_read_all_data角色或超级用户才默认具备)。
验证方式:SELECT has_table_privilege('public.users', 'SELECT'); 返回 t 才算有基本权限;SELECT pg_get_triggerdef(oid) FROM pg_trigger LIMIT 1; 若返回 NULL,大概率是权限不足。
生产环境批量导出前,务必用目标账号先试一条,别等到恢复时报一堆permission denied才反应过来。

















