【问题标题】:Capture columns in plpgsql during UPDATE在 UPDATE 期间捕获 plpgsql 中的列
【发布时间】:2013-02-27 15:34:50
【问题描述】:

我正在 plpgsql 中为 Postgres 9.1 编写触发器。我需要能够捕获在 UPDATE 的 SET 子句中发出的列名,以便我可以在审计表中记录指定的操作。 Postgres 文档中的示例很简单,不足以满足我的需要。我已经在互联网上搜索了好几天,但我找不到任何其他试图实现我想要在这里做的事情的例子。

我的时间很紧,要尽快解决这个问题。我不知道 Tcl 所以 pl/Tcl 在这一点上对我来说是不可能的。 pl/Perl 可能有用,但我不知道从哪里开始。此外,如果可能的话,我想在 pl/pgsql 中找到一种方法来实现这一点,以实现可移植性和维护。如果有人可以为此推荐一个 pl/Perl 解决方案,我将不胜感激。

这里是要审计的目标表的表结构:

注意:记录表中还有许多其他列,但为了简单起见,我没有在这里列出它们。但触发器应该能够记录对行中任何列的更改。

CREATE TABLE record (
   record_id    integer NOT NULL PRIMARY KEY,
   lastname     text,
   frstname     text,
   dob          date,
   created      timestamp default NOW(),
   created_by   integer,
   inactive     boolean default false
);
create sequence record_record_id_seq;
alter table record alter record_id set default nextval('record_record_id_seq');

这是我的审计表:

CREATE TABLE record_audit (
   id                integer NOT NULL PRIMARY KEY,
   operation         char(1) NOT NULL, -- U, I or D
   source_column     text,
   source_id         integer,
   old_value         text,
   new_value         text,
   created_date      timestamp default now(),
   created_by        integer
);
create sequence record_audit_id_seq;
alter table record_audit alter id set default nextval('record_audit_id_seq');

我的目标是将 INSERTS 和 UPDATES 记录到 record_audit 表中的记录表中​​,这不仅会详细说明更新的目标 record_id (source_id) 和更新的列 (source_column),还会详细说明 old_value 和列的新值。

我知道列值必须是 CAST() 到一种文本。我相信我可以通过访问 NEW 和 OLD 来访问 old_value 和 new_value,但是我很难弄清楚如何获取 UPDATE 查询的 SET 子句中使用的列名。我需要触发器为 SET 子句中指定的每一列向 record_audit 表添加一条新记录。请注意,没有 DELETE 操作,因为记录只是 UPDATED 到 inactive = 't' (因此记录在审计表中)

到目前为止,这是我的触发器(显然不完整)。请见谅,我边走边学pl/pgsql。

-- Trigger function for record_audit table
CREATE OR REPLACE FUNCTION audit_record() RETURNS TRIGER AS $$
   DECLARE
      insert_table  text;
      ref_col       text; --how to get the referenced column name??

   BEGIN
      --
      -- Create a new row in record_audit depending on the operation (TG_OP)
      --
      IF (TG_OP = 'INSERT') THEN

         -- old_value and new_value are meaningless for INSERTs. Just record the new ID.
         INSERT INTO record_audit 
            (operation,source_id,created_by) 
         VALUES
            ('I', NEW.record_id, NEW.created_by);


      ELSIF (TG_OP = 'UPDATE') THEN

         FOR i in 1 .. TG_ARGV[0] LOOP
            ref_col := TG_ARGV[i].column; -- I know .column doesn't exist but what to use?

            INSERT INTO record_audit 
               (operation, source_column, source_id, old_value, new_value, created_by) 
            VALUES 
               ('U', ref_col, NEW.record_id, OLD.ref_col, NEW.ref_col, NEW.created_by); 
         END LOOP;

      END IF;
      RETURN NULL; -- result is ignored anyway since this is an AFTER trigger
   END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER record_audit_trig
AFTER INSERT OR UPDATE on record
   FOR EACH ROW EXECUTE PROCEDURE audit_record();

感谢您阅读这个冗长而曲折的问题!

【问题讨论】:

  • 了解 SET 中的内容是否至关重要。因为我觉得你做不到。很容易记录哪些字段发生了变化。
  • 谢谢理查德。我需要能够为每一个更改的列添加一个新记录到 record_audit,这就是我希望能够访问 SET 子句的原因。您提到记录哪些字段已更改很容易。我将如何做到这一点?

标签: postgresql triggers plpgsql


【解决方案1】:

您无法获得此信息 - 不是 PL 级别 - 可能在 C 中是可能的。

足够好的解决方案是基于记录 NEW 和 OLD 中更改的字段。您可以从系统表中获取字段列表〜与连接到触发器的表相关。

【讨论】:

  • 所以也许要走的路就是简单地比较 NEW 和 OLD 的值,看看发生了什么变化。但是那怎么知道哪一列被改变了呢?
  • Google 围绕 hstore 审计日志记录 - 这是获取更改列的(文本表示)的最简单方法。
  • 经过大量研究后,我得出结论,正如理查德建议的那样,解决整个问题的最简单方法是使用 hstore 数据类型。因为我已经在运行 Postgres 9.1,所以安装起来很简单,而且我很快就在我的 tigger 函数中找到了它的巨大用途。感谢您的所有建议。尤其是你的 Richard,建议使用 hstore。
猜你喜欢
  • 1970-01-01
  • 2018-12-06
  • 1970-01-01
  • 1970-01-01
  • 2015-05-04
  • 2019-02-26
  • 2013-01-01
  • 1970-01-01
  • 2014-06-05
相关资源
最近更新 更多