如何跟踪PostgreSQL中任何函数的更改

如何跟踪PostgreSQL中任何函数的更改,sql,postgresql,triggers,postgresql-9.4,Sql,Postgresql,Triggers,Postgresql 9.4,我希望跟踪PostgreSQL中任何函数的更改 示例-假设我在postgesql数据库中有函数fun\u name(),并且我正在对函数进行修改 现在,我想跟踪记录,比如 DateTime,Schema_name,Function_name,old_func_text,new_func_text 请建议在postgresql中实现这一点的最佳方法 我正在研究事件触发器 谢谢。在Postgres 9.5中,有一个函数可用于事件触发器中,以获取插入/更改对象的oid 日志表: create tab

我希望跟踪PostgreSQL中任何函数的更改

示例-假设我在postgesql数据库中有函数
fun\u name()
,并且我正在对函数进行修改

现在,我想跟踪记录,比如

DateTime,Schema_name,Function_name,old_func_text,new_func_text
请建议在postgresql中实现这一点的最佳方法

我正在研究事件触发器


谢谢。

Postgres 9.5中,有一个函数可用于事件触发器中,以获取插入/更改对象的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
command标记,然后使用
pg\u event\u trigger\u DROP\u objects()
函数,类似于
pg\u event\u trigger\u ddl\u commands()


不幸的是,在Postgres 9.4中没有
pg\u事件\u触发器\u ddl\u命令()。您可以尝试使用
current\u query()
获取插入/更改的对象,或者在
C
中编写触发器函数。我认为更简单的方法是将Postgres升级到9.5+。

关于限制的一些注意事项

  • 您不能在PostgreSQL中的系统表上放置触发器,因此无法在这方面创建审计跟踪
  • 您可能需要使用PostgreSQL外部的东西来管理版本控制。所以我推荐像sqitch这样的东西
  • pgdump的一个主要问题是不能保证事物的顺序是唯一的,所以简单的dump和version会给你一个非常糟糕的信噪比

    现在您可以做的一件事是使用ddl捕获事件,然后记录和处理。这仍然需要您在设计方面付出大量努力,但这是完全可能的。如果您使用事件触发器,请首先在登台系统上进行彻底测试


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

    完整版本,带有拖放和触发器跟踪功能

    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();
    

    谢谢你的解决方案。您能否建议我是否可以将日志存储在数据库表中。由于pg_dump不能保证函数之类的排序,您希望信噪比是多少?我对
    pg_dump
    的第一个回答可能过于笼统,因此我完全修改了答案。@klin感谢您的努力。非常类似于我试图找到的解决方案。