禁用特定DELETE查询的触发器

时间:2014-02-25 05:45:04

标签: sql ruby psql

我有一个红宝石应用程序。该应用程序正在对特定表进行插入,更新和删除。

它有两种INSERT,一个插入应该在表中插入一条记录,也插入到trigger_logs表中。另一个插入只是将记录插入表中而不执行任何操作。另一种方法是,一种插入应该记录'insert'发生在另一个表中,另一种插入应该是一个普通的插入。同样,还有两种UPDATE和DELETE。

我使用trigger_disable实现了两种类型的INSERT和UPDATE。请参阅下面的触发器代码。

因此,当我执行INSERT时,如果我不想记录触发器,我会将trigger_disable布尔值设置为true。同样地,我也在做更新。

但是我无法区分两种DELETE,就像我对INSERT或UPDATE这样。将记录两种DELETE的DELETE操作。

注意:我正在记录在特定情况下所做的所有更改,这些更改将由ruby应用程序确定。如果不满足条件,我只需要相应地进行正常的INSERT,UPDATE或DELETE。

  CREATE OR REPLACE FUNCTION notify_#{@table_name}()
      RETURNS TRIGGER
      LANGUAGE plpgsql
      AS $$
        DECLARE
          changed_row_id varchar(100);
        BEGIN

          IF TG_OP = 'DELETE' THEN
          -- When the trigger is due to a delete

            IF (OLD.trigger_disable IS NULL) 
               OR (OLD.trigger_disable = false) THEN
            -- Prevent the trigger if trigger_disable is 'true'
            -- The Problem is here: This insertion into the 
            -- trigger_logs table happens
            -- for all the delete statements. 
            -- But during certain deletes I should not
            -- insert into trigger_logs

              INSERT INTO trigger_logs (table_name, action, row_id, dirty) 
              VALUES (
                '#{@table_name}', 
                CAST(TG_OP AS Text), 
                OLD.id,
                true
              ) RETURNING id into changed_row_id;

            END IF;

            RETURN OLD;

          ELSE
          -- The trigger is due to a Insert or Update

            IF (NEW.trigger_disable IS NULL) 
               OR (NEW.trigger_disable = false) THEN
            -- Prevent the trigger if trigger_disable is 'true'

              INSERT INTO trigger_logs (table_name, action, row_id, dirty) 
              VALUES (
                '#{@table_name}',
                CAST(TG_OP AS Text),
                NEW.id,
                true
              ) RETURNING id into changed_row_id;

            ELSE
              NEW.trigger_disable := false;

            END IF;

            RETURN NEW;
          END IF;

        END

1 个答案:

答案 0 :(得分:1)

我打算在黑暗中捅一下,并猜测你是在尝试从上下文中控制触发器是否被触发。

如果是这样,也许您可​​以使用会话变量?

BEGIN;

SET LOCAL myapp.fire_trigger = 'false';

DELETE FROM ...;

COMMIT;

并在触发器中测试它:

IF current_setting('myapp.fire_trigger') = 'true' THEN

但请注意,如果会话中缺少设置,则不会出现NULL,您将收到错误消息:

regress=> SELECT current_setting('myapp.xx');
ERROR:  unrecognized configuration parameter "myapp.xx"

所以你想要:

ALTER DATABASE mydb SET myapp.fire_trigger = 'true';

另请注意,参数为text而非布尔值。

最后,会话变量没有安全性。因此,它对安全审计没有用,因为任何人都可以来SET myapp.fire_trigger = 'false'

(如果这不符合您的需求,您可能需要重新考虑是否应该使用触发器而不是应用程序级别执行此操作。)