pg_class.relhastriggers是`ALTER TABLE ... DISABLE TRIGGER USER`的安全替代品吗?

时间:2016-07-12 19:34:11

标签: postgresql

在我的桌子上,我有一个阻止更新的触发器,因此意味着行在插入后实际上是不可变的。

当需要对此表执行追溯更新时(例如添加新的计算字段),我采取了以下方法:

ALTER TABLE my_table DISABLE TRIGGER USER;

UPDATE my_table
  SET x = (...);

ALTER TABLE my_table ENABLE TRIGGER USER;

这种方法的缺点是它需要一个AccessExclusiveLock。

我想知道以下是否可以安全使用,因为保证UPDATE中的行不会被其他查询更新:

BEGIN;
  UPDATE pg_class
    SET relhastriggers = FALSE
    WHERE relname = 'my_table';

  UPDATE my_table
    SET x = (...);

  UPDATE pg_class
    SET relhastriggers = TRUE
    WHERE relname = 'my_table';
COMMIT;

到目前为止我所做的尝试表明这是安全的,并且在此交易之外,触发器将继续正常应用。

另外,如果我的用例确实安全,那么它在哪些情况下是不安全的?

我正在使用 Postgres 9.4.8

谢谢: - )

1 个答案:

答案 0 :(得分:1)

更新relhastriggers的行为更像DISABLE TRIGGER ALL,即它还会禁用用于外键和延迟唯一性检查的内部触发器。在您的情况下,这可能是也可能不是问题。

我不知道pg_class的直接更新是否会违反Postgres代码库某些角落的某些假设。但总的来说,黑客目录表本质上是不安全的;即使它没有破坏任何现在,也不能保证将来的版本会出现这种情况。

REVOKE UPDATE ON my_table是阻止更新的更好方法。超级用户可自动获得免费使用,因此获得[{1}}权限的任何人都可以对UPDATE pg_class没有任何问题。

如果出于某种原因,你真的需要用触发器来做这件事,那么还有另一种(有些hacky,但至少得到支持)绕过它的方法。默认情况下,复制进程不会触发触发器(尽管可以通过ALTER TABLEmy_table子句来控制)。这意味着您可以通过模拟复制器来绕过触发器:

ENABLE ALWAYS

与目录更新一样,这也将禁用任何内部约束触发器。