【问题标题】:Postgresql create a log schemaPostgresql 创建日志模式
【发布时间】:2016-01-29 08:02:16
【问题描述】:

所以我的问题很简单。我有一个架构prod 有很多表,另一个log 有完全相同的表和结构(主键改变就是这样)。 当我在架构prod 中执行UPDATEDELETE 时,我想在log 架构中记录旧数据。

我在更新或删除后调用了以下函数:

CREATE FUNCTION prod.log_data() RETURNS trigger
LANGUAGE plpgsql AS $$
DECLARE
    v RECORD;
    column_names text;
    value_names text;
BEGIN

    -- get column names of current table and store the list in a text var
    column_names = '';
    value_names = '';
    FOR v IN SELECT * FROM information_schema.columns WHERE table_name = quote_ident(TG_TABLE_NAME) AND table_schema = quote_ident(TG_TABLE_SCHEMA) LOOP
        column_names = column_names || ',' || v.column_name;
        value_names = value_names || ',$1.' || v.column_name;
    END LOOP;

    -- remove first char ','
    column_names = substring( column_names FROM 2);
    value_names = substring( value_names FROM 2);

    -- execute the insert into log schema
    EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' ( ' || column_names || ' ) VALUES ( ' || value_names || ' )' USING OLD;

    RETURN NULL; -- no need to return, it is executed after update
END;$$;

烦人的部分是我必须从information_schema 获取每一行的列名。 我宁愿用这个:

    EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' SELECT ' || OLD;

但有些值可以是NULL,所以这会执行:

INSERT INTO log.user SELECT 2,,,"2015-10-28 13:52:44.785947" 代替 INSERT INTO log.user SELECT 2,NULL,NULL,"2015-10-28 13:52:44.785947"

有没有办法将",," 转换为",NULL,"

谢谢

-昆汀

【问题讨论】:

    标签: postgresql function triggers audit audit-logging


    【解决方案1】:

    首先我必须说,在我看来,使用 PostgreSQL 系统表(如information_schema)是这种用例的正确方法。特别是您必须编写一次:您创建函数prod.log_data() 并完成。此外,在这种情况下使用OLD(就像*)可能会很危险,因为没有指定元素顺序。

    但是

    要回答您的确切问题,我知道的唯一方法是对OLD 进行一些操作。只需观察您通过连接... ' SELECT ' || OLDOLD 转换为text。默认转换会创建丑陋的双逗号。因此,接下来您可以使用该文本。最后我建议:

    DECLARE
         tmp TEXT
    ...
    BEGIN
    ...
         /*to make OLD -> text like (2,,3,4,,)*/
         SELECT '' || OLD INTO tmp; /*step 1*/
    
         /*take care of commas at the begining and end: '(,' ',)'*/
         tmp := replace(replace(tmp, '(,', '(NULL,'), ',)', ',NULL)'); /*step 2*/
    
         /* replace rest of commas to commas with NULL between them */
         SELECT array_to_string(string_to_array(tmp, ',', ''), ',', 'NULL') INTO tmp; /*step 3*/
    
         /* Now we can do EXECUTE*/
         EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' SELECT ' || tmp;
    

    当然你可以一步完成1-3步

    SELECT array_to_string(string_to_array(replace(replace('' || NEW, '(,', '(NULL,'), ',)', ',NULL)'), ',', ''), ',', 'NULL') INTO tmp;
    

    在我看来,这种方法并没有比使用 information_schema 更好,但这是你的决定。

    【讨论】:

    • 非常感谢!感谢您的意见,我将在大数据库上进行一些测试,看看 information_schema 是否对我的用例来说太慢了。
    • 其实这是为了性能。但是你能告诉我postgres什么时候触发吗?我想知道 postgre 是否在触发触发器 after INSERT 之前返回到 php
    • 我不是 100% 肯定,但我怀疑它是。 PostgreSQL 中的每个触发器(无论是AFTER 还是BEFORE)都在与主查询相同的事务中执行。这意味着在触发器结束之前执行不会结束。请注意,AFTER 触发器中未处理的异常会使整个事务回滚。因此,如果 php 认为即使 db 服务器本身还没有新的查询成功,也可能会出现问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-14
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 2015-08-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多