2

在我的 Rails 3.1.3 应用程序中,我有subscriptions表格;必须跟踪此表上的操作,这对于计费至关重要。由于可以通过多种方式(API、控制台、客户端应用程序)访问数据库,因此简单的 ActiveRecord 回调或观察者不足以确保记录表上的所有事务。因此,我在表上创建了一个数据库触发器,该触发器在subscriptions任何时候发生变化时都会在“日志”表中插入一条记录。为了做到这一点,我正在使用这样的 Rails 迁移:

def up
   execute <<-SQL
     CREATE OR REPLACE FUNCTION FUNCTION_Event_Type() 
     RETURNS TRIGGER 
     AS 
     $TRIGGER_Event_Type$
      BEGIN
        IF (TG_OP = 'DELETE') THEN
              INSERT INTO subscription_log (company_id, product_id, old_package_id, new_package_id, trigger_type, updated_at, created_at)
               SELECT OLD.company_id, OLD.product_id, OLD.package_id, NULL, 'Delete', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP;
              RETURN OLD;
          ELSIF (TG_OP = 'UPDATE') THEN
              INSERT INTO subscription_log (company_id, product_id, old_package_id, new_package_id, trigger_type, updated_at, created_at) 
              SELECT OLD.company_id, OLD.product_id, OLD.package_id, NEW.package_id, 'Update', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP;
              RETURN NEW;
          ELSIF (TG_OP = 'INSERT') THEN
              INSERT INTO subscription_log (company_id, product_id, old_package_id, new_package_id, trigger_type, updated_at, created_at) 
              SELECT NEW.company_id, NEW.product_id, NULL, NEW.package_id, 'Insert', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP;
              RETURN NEW;
          END IF;
        RETURN NULL;
      END;
  $TRIGGER_Event_Type$ 
  LANGUAGE plpgsql;

  CREATE TRIGGER TRIGGER_Event_Type
  AFTER INSERT OR UPDATE OR DELETE ON subscriptions
      FOR EACH ROW EXECUTE PROCEDURE FUNCTION_Event_Type();
 SQL
end

触发器工作正常并根据需要记录。我有 rspec 测试检查记录是否插入到“记录”表中,只要在表上完成某些操作subscriptions。但是,我继续开发该应用程序,并且不得不subscriptions使用 Rails 迁移向表中添加一列:

def change
  add_column :subscriptions, :description, :text
end

运行迁移后,我的测试检查触发器功能如下:

  lambda do
    FactoryGirl.create(:subscription)
  end.should change(SubscriptionLog, :count).by(1)

开始失败。

更新:添加列后,开发数据库仍然有触发器。运行添加列的迁移后,测试数据库丢失触发器......奇怪

问题:
更改表会杀死触发器吗?如果确实如此,那么确保触发器持续存在的方法是什么?

4

2 回答 2

3

最后,我想通了。在表中添加一列后subscriptions,我跑了rake db:test:prepare(不要问我为什么)。显然,“转储当前模式,吹走测试数据库,然后从转储中重建测试模式”由于触发器是通过执行 SQL(而不是 rails 助手)创建的,因此它从未反映在模式文件中。因此,解决方案从未重新创建它rake db:test:prepare:删除测试数据库并再次运行所有迁移......并且永远不会rake db:test:prepare再次运行。希望有人觉得它有用。

于 2012-04-26T18:22:53.017 回答
2

更改表会杀死触发器吗?

回答问题:,一般不会。添加列根本不应该与触发器混淆 - 只要它不会间接影响触发器函数内的 SQL 语句或触发器的WHEN条件(PostgreSQL 9.0 或更高版本)。


如果您通过删除/重新创建表来添加列,那么当然,您也必须重新创建触发器。触发器函数不是随表删除,而是触发器是。你能检查一下触发器是否还在吗?


您有INSERT没有目标列表的命令。这是一种常见的足枪,当基础表稍后更改时容易损坏。除了 ad-hoc SQL 命令,总是提供一个目标列表。

代替:

INSERT INTO subscription_log
SELECT OLD.company_id, OLD.product_id, OLD.package_id, ... ;

做:

插入订阅日志(col1,col2,col3,...)
选择 OLD.company_id, OLD.product_id, OLD.package_id, ... ;

手册中有关 INSERT 的详细信息。

于 2012-04-26T15:03:13.990 回答