【问题标题】:How to track changes in any function in PostgreSQL如何跟踪 PostgreSQL 中任何函数的变化
【发布时间】:2017-02-25 11:39:59
【问题描述】:

我想跟踪 PostgreSQL 中任何函数的变化。

示例 - 假设我在 postgesql 数据库中有函数 fun_name(),我正在修改函数。

现在,我想跟踪像

这样的记录
DateTime,Schema_name,Function_name,old_func_text,new_func_text

请建议在 postgresql 中实现这一目标的最佳方法。

我正在https://www.postgresql.org/docs/9.3/static/sql-createeventtrigger.html研究事件触发器

谢谢。

【问题讨论】:

    标签: sql postgresql triggers postgresql-9.4


    【解决方案1】:

    Postgres 9.5 中有一个函数 pg_event_trigger_ddl_commands() 可用于事件触发器中以获取插入/更改对象的 oid。

    日志表:

    create table function_log (
        datetime timestamp, 
        schema_name text, 
        function_name text, 
        tag text, 
        function_body text);
    

    事件函数和触发器:

    create or replace function public.on_function_event()
        returns event_trigger
        language plpgsql
    as $function$
    begin
        insert into function_log
        select now(), nspname, proname, command_tag, prosrc
        from pg_event_trigger_ddl_commands() e
        join pg_proc p on p.oid = e.objid
        join pg_namespace n on n.oid = pronamespace;
    end
    $function$;
    
    create event trigger on_function_event
    on ddl_command_end 
    when tag in ('CREATE FUNCTION', 'ALTER FUNCTION')
    execute procedure on_function_event();
    

    例子:

    create or replace function test()
    returns int as $$ select 1; $$ language sql;
    
    create or replace function test()
    returns int as $$ select 2; $$ language sql;
    
    alter function test() immutable;
    
    select *
    from function_log;
    
              datetime          | schema_name | function_name |       tag       | function_body 
    ----------------------------+-------------+---------------+-----------------+---------------
     2017-02-26 13:05:15.353879 | public      | test          | CREATE FUNCTION |  select 1; 
     2017-02-26 13:05:15.353879 | public      | test          | CREATE FUNCTION |  select 2; 
     2017-02-26 13:05:15.353879 | public      | test          | ALTER FUNCTION  |  select 2; 
    (3 rows)
    

    您可以将DROP FUNCTION 命令标签添加到触发器,然后使用pg_event_trigger_dropped_objects() 函数,类似于pg_event_trigger_ddl_commands()

    很遗憾,Postgres 9.4 中没有 pg_event_trigger_ddl_commands()。您可以尝试使用current_query() 获取插入/更改的对象,或在C 中编写触发函数。我认为更简单的方法是将 Postgres 升级到 9.5+。

    【讨论】:

    • 感谢您的解决方案。您能否建议我是否可以将日志存储在数据库表中。
    • 由于 pg_dump 不保证函数之类的东西的顺序,你认为信噪比会是多少?
    • 我对@9​​87654332@的第一个答案可能太笼统了,所以我完全修改了答案。
    • @klin 感谢您的努力。与我试图找到的解决方案非常相似。
    【解决方案2】:

    关于约束的几点说明。

    1. 您不能在 PostgreSQL 中的系统表上放置触发器,因此无法在这方面创建审计跟踪。
    2. 您可能需要使用 PostgreSQL 外部的东西来管理版本控制。所以我会推荐类似 sqitch 的东西。

    pgdump 的一个主要问题是不能保证事物的顺序是唯一的,所以简单的转储和版本会给你一个非常糟糕的信噪比。

    现在您可以做的一件事是在 ddl 上使用event triggers 来捕获事件,然后记录和处理。这仍然需要您在设计方面付出很多努力,但完全有可能。如果您使用事件触发器,请先在暂存系统上彻底测试!

    我会说,当我需要做这样的事情时,我通常使用事件触发器和系统表的副本,所以我从旧目录更新表并记录更改。

    【讨论】:

      【解决方案3】:

      带有拖放和触发跟踪的完整版

      create schema hist;
      create table hist.functions (
          datetime timestamp, 
          schema_name text, 
          function_name text, 
          oper char(1), 
          function_body text,
          ip text);
      
        create or replace function hist.on_function_event_hist()
          returns event_trigger
          language plpgsql
      as $function$
      begin
      
         insert into hist.functions
         select now(),
         coalesce(nspname, t.trigger_schema),
         coalesce(proname, t.trigger_name),
         command_tag::char(1),
         coalesce(pg_get_functiondef(p.oid),
         'create trigger ' || trigger_name || ' ' || action_timing || ' ' || event_manipulation ||
              E'\non\n' || quote_ident(event_object_schema) || '.' || quote_ident(event_object_table) || ' for ' || action_orientation || coalesce(E'\rwhen (' || action_condition || E')\r', E'\r')  || action_statement || ';'),
         coalesce(inet_client_addr()::text, '::1')
          from pg_event_trigger_ddl_commands() e
           left join pg_proc p on p.oid = e.objid
          left    join pg_namespace n on n.oid = pronamespace
          left join information_schema.triggers t on object_identity like t.trigger_name || '%';
      end
      $function$;
      
      create event trigger on_function_event
      on ddl_command_end
      when tag in ('CREATE FUNCTION', 'ALTER FUNCTION', 'ALTER TRIGGER', 'CREATE TRIGGER')
      execute procedure hist.on_function_event_hist();
      
        create or replace function hist.on_function_drop_func()
          returns event_trigger
          language plpgsql
      as $function$
      begin
      
         insert into hist.functions
         SELECT now(),
         schema_name,
         object_identity,
         'D',
         null,
          coalesce(inet_client_addr()::text, '::1')
          FROM pg_event_trigger_dropped_objects();
      end
      $function$;
      
      CREATE EVENT TRIGGER on_function_drop
         ON sql_drop
         EXECUTE PROCEDURE hist.on_function_drop_func();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-21
        • 1970-01-01
        • 2013-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多