2

我有以下基于另一个 SE 问题(PostgreSql 上的哈希环)

CREATE TABLE sms.tablename
(
  id uuid,
  mdate date
)

和分区。

CREATE TABLE sms.tablename_partition_1 ( CHECK ( sms.hash(id) = '1' ) ) INHERITS (sms.tablename);
...
CREATE TABLE sms.tablename_partition_f ( CHECK ( sms.hash(id) = 'f' ) ) INHERITS (sms.tablename);

现在问题来了。

当我添加此触发器时。

CREATE TRIGGER "delete_me"
  BEFORE DELETE
  ON sms.tablename
  FOR EACH ROW
  EXECUTE PROCEDURE sms.delete_me(E'\\x');

CREATE OR REPLACE FUNCTION sms.delete_me()
  RETURNS trigger AS
$BODY$

begin

    RAISE NOTICE 'HERE !!!';
    return OLD;

end;
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER
  COST 100;

这个触发器永远不会运行......我看不到 NOTICE 消息。现在,如果我将相同的触发器应用于另一个表(非分区),它工作正常,它完成了它的工作,该行被删除并弹出通知消息。

更多信息:“i686-pc-linux-gnu 上的 PostgreSQL 9.1.6,由 gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3 编译,32 位”

我只是想避免在表上使用存储过程并保持 ORM 清洁。

编辑:

1) 表或任何继承的表上不存在任何规则,同样适用于索引或 PK。

2)运行以下命令。

DELETE FROM sms.tablename WHERE id = 'a5e52a04-282f-4cf4-8347-a43d68725e6b';

3) 显示此问题的完整 SQL。

http://pastebin.com/mZBFEtaY
4

2 回答 2

2

删除触发器在包含该行的子表上触发,而不是在父表上。您必须将触发器添加到每个子表。

DO                                                                
$$
DECLARE
  h text;
BEGIN
  FOR h IN SELECT to_hex(x) FROM generate_series(0,15) x LOOP
    EXECUTE format('CREATE TRIGGER "DeleteRedirector" BEFORE DELETE ON pproblem.%I FOR EACH ROW EXECUTE PROCEDURE pproblem.delete_me('''');',
                   'tablename_partition_'||h);
  END LOOP;
END;
$$;

演示:

regress=# begin;
BEGIN
regress=# DELETE FROM pproblem.tablename;
NOTICE:  HERE !!!
NOTICE:  HERE !!!
NOTICE:  HERE !!!
DELETE 3
regress=# rollback;
ROLLBACK
于 2012-10-30T04:43:19.343 回答
0

这里发生的情况可能是您的触发器以意想不到的方式与另一个触发器或规则交互。

DELETE如果来自分区的较早触发器s 然后执行 aRETURN NULL以防止将操作应用于其自身的基表,则稍后的触发器不会触发,因为该操作已被取消(并被重定向,但后来的触发器不知道) .

视图出现了类似但更令人困惑的情况,其中DO INSTEAD平均查询被重写,因此它们从一开始就不会引用基表。

如果您在分区表上使用触发器,则需要在每个分区以及主表上创建它们,或者注意触发器的顺序。触发器作为 BEFORE触发器运行,然后AFTER按触发器名称的字母顺序触发。

给定 DDL:

create table demo (id integer);

CREATE OR REPLACE FUNCTION cancel_tg() RETURNS trigger AS $$
BEGIN
  RETURN NULL;
END;
$$ LANGUAGE plpgsql VOLATILE;

CREATE OR REPLACE FUNCTION notice_tg() RETURNS trigger AS $$
BEGIN
  RAISE NOTICE 'Trigger fired';
  -- This just makes it a no-op:
  IF tg_op = 'INSERT' OR tg_op = 'DELETE' THEN
    RETURN NEW;
  ELSE
    RETURN OLD;
  END IF;
END;
$$ LANGUAGE plpgsql VOLATILE;

通知触发器之前的取消触发器不产生任何消息:

regress=> CREATE TRIGGER aa_cancel 
          BEFORE INSERT OR UPDATE OR DELETE ON demo
          FOR EACH ROW EXECUTE PROCEDURE cancel_tg();
CREATE TRIGGER
regress=> CREATE TRIGGER bb_notice
          BEFORE INSERT OR UPDATE OR DELETE ON demo 
          FOR EACH ROW EXECUTE PROCEDURE notice_tg();
CREATE TRIGGER
regress=> insert into demo values (1);
INSERT 0 0

反过来会触发通知,因为aa_notice在之前触发bb_cancel

regress=> drop trigger bb_notice on demo;
DROP TRIGGER
regress=> drop trigger aa_cancel on demo;
DROP TRIGGER
regress=> CREATE TRIGGER aa_notice
          BEFORE INSERT OR UPDATE OR DELETE ON demo
          FOR EACH ROW EXECUTE PROCEDURE notice_tg();
CREATE TRIGGER
regress=> CREATE TRIGGER bb_cancel
          BEFORE INSERT OR UPDATE OR DELETE ON demo 
          FOR EACH ROW EXECUTE PROCEDURE cancel_tg();
CREATE TRIGGER
regress=> insert into demo values (1);
NOTICE:  Trigger fired
INSERT 0 0

这意味着AFTER触发器永远不会在使用触发器分区的表上触发,并且BEFORE触发器必须按字母顺序排列在分区触发器之前。在 PostgreSQL 中进行分区的另一种方式有点丑陋......

于 2012-10-29T10:14:21.570 回答