【问题标题】:How to create one function for all triggers by using Dynamic table name in trigger functionh如何使用触发器函数中的动态表名为所有触发器创建一个函数
【发布时间】:2021-06-09 05:50:18
【问题描述】:

我有 3 个触发器用于 3 个历史表和 3 个函数。 如何通过在触发器函数中使用动态表名为所有触发器创建一个函数。这样我就可以动态传递表名并相应地插入新旧记录..

示例 1 个触发器和 1 个功能代码供参考:

CREATE TABLE emp (
    empname           text NOT NULL,
    salary            integer
);

CREATE TABLE emp_audit(
    operation         char(1)   NOT NULL,
    stamp             timestamp NOT NULL,
    userid            text      NOT NULL,
    empname           text      NOT NULL,
    salary integer
);

CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
    BEGIN
        --
        -- Create a row in emp_audit to reflect the operation performed on emp,
        -- making use of the special variable TG_OP to work out the operation.
        --
        IF (TG_OP = 'DELETE') THEN
            INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
        ELSIF (TG_OP = 'UPDATE') THEN
            INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$emp_audit$ LANGUAGE plpgsql;

CREATE TRIGGER emp_audit`enter code here`
AFTER INSERT OR UPDATE OR DELETE ON emp
    FOR EACH ROW EXECUTE FUNCTION process_emp_audit();

我想使用从静态历史表到动态表,以便我可以为所有 3 个触发器使用一个函数。如何动态更改下面的插入语句。

INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
 INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*;

【问题讨论】:

  • 那你需要使用dynamic SQL
  • 如何在下面的插入语句中使用动态sql。以便为 3 个历史记录表上的所有 3 个触发器提供一个函数。
  • 如何使用动态 sql 插入动态历史表,以便我可以将 dml 记录存储到 hsitory 表中..我有 3 个历史表和 3 个函数。想要 3 个触发器有 1 个功能,并且应该在插入语句中使用动态 hsitory 表

标签: postgresql triggers dynamic-sql


【解决方案1】:

您可以对触发器函数使用动态 SQL 和参数:

CREATE FUNCTION audit() RETURNS TRIGGER AS
$$
   ...
   EXECUTE
      format(
         'INSERT INTO %I SELECT ''I'', current_timestamp, %L, ($1::%I.%I).*',
         TG_ARGV[0], current_user, TG_TABLE_SCHEMA, TG_TABLE_NAME
      )
      USING NEW;
   ...
$$;

CREATE TRIGGER emp_audit_trig
   AFTER INSERT OR UPDATE OR DELETE ON emp
   FOR EACH ROW
   EXECUTE FUNCTION audit('emp_audit');

您传递给触发函数的第一个参数将在函数内部以TG_ARGV[1] 的形式提供。

使用format构造SQL字符串,同时避免SQL注入风险。占位符 %I 用于 SQL 标识符,%L 用于字符串文字。

$1 将替换为USING 的第一个参数。

【讨论】:

  • 我收到错误:NULL 值不能格式化为 sql 标识符
  • 对不起,我忘了索引是从 0 开始的。已修复。
  • 我收到错误“关系 emp_audit 不存在。”你能把代码放在小提琴中并执行..
  • 当然要建表:^)
  • 以上代码在没有架构的情况下也能正常工作。现在我将模式“abc”添加到所有对象,如 abc.emp、abc.emp_audit、abc.audit(),并且在最终触发代码传递参数中,我还提到了 EXECUTE FUNCTION audit('abc.emp_audit');。我得到错误关系不存在..你能帮我当模式名称用于表和函数时如何使用相同的函数和触发器
【解决方案2】:

如果您想要完全动态而不需要动态 SQL,那么您可以将整个行转换并存储为 JSON(B) 或 hstore。以下演示(以及几个额外的审计列):

-- Audit table and Trigger function
create table audit_table ( aud_id integer generated always as identity
                         , action        text 
                         , table_name    text 
                         , action_tstz   timestamptz default clock_timestamp()
                         , row_data      jsonb                         
                         , user_name     text        default user
                         , sql_stmt      text        default current_query()
                         ) ; 

create or replace 
function dml_action_audit()
 returns trigger
 language plpgsql
as $$
begin 
    insert into audit_table( action
                           , table_name
                           , row_data) 
       select tg_op
            , tg_table_schema ||'.' || tg_table_name
            , case when tg_op = 'INSERT' 
                   then row_to_json(new.*)
                   else row_to_json(old.*)
              end ; 
      return null;
end; 
$$; 

并使用带有函数定义的标准表。它还导致了一些有趣的可能性。见Demo here

【讨论】:

    猜你喜欢
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-30
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    相关资源
    最近更新 更多