如何将 PostgreSQL 触发器内的新数据存储为 JSON?

问题描述 投票:0回答:2

我尝试按照以下说明进行操作:

https://wiki.postgresql.org/wiki/Audit_trigger

以 JSON 形式审核值

对于 PostgreSQL 9.2 或带有出色的 json_91 插件的 9.1,您可以 将表中的旧值和新值记录为结构化 json,而不是 纯文本,使您能够更强大地查询审核历史记录。 只需更改 v_old_data、v_new_data、original_data 和 new_data 从 TEXT 转换为 json,然后替换 ROW(OLD.) 和 ROW(NEW.) 分别使用 row_to_json(旧) 和 row_to_json(新)。

但是这给了我一个错误:

CREATE OR REPLACE FUNCTION add_log (name text, Action TEXT, data jsonb, OUT RETURNS BOOLEAN)
AS $$
BEGIN
    RETURNS = true;
END;
$$
LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION log_city() RETURNS TRIGGER AS 
$$
DECLARE
v_new_data jsonb;
BEGIN
    IF (TG_OP = 'UPDATE') THEN
        RETURN NEW;
    ELSIF (TG_OP = 'INSERT') THEN
        v_new_data := row_to_jsonb(NEW);
        EXECUTE add_log('City', 'City.New', v_new_data);
        RETURN NEW;
    END IF;
    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;

INSERT INTO Location (city, state, country) values ('a', 'b' , 'c')

它说:

错误:函数 row_to_jsonb(location) 不存在

如果我输入

v_new_data := row_to_jsonb(ROW(NEW));
那么我会得到:

错误:函数 row_to_jsonb(record) 不存在

json postgresql triggers postgresql-9.5
2个回答
2
投票

文档中指出

表9-42显示了可用于创建json的函数 和 jsonb 值。 (jsonb 没有等效的函数, row_to_json 和 array_to_json 函数。但是,to_jsonb function 提供与这些函数大致相同的功能 会的。)

因此必须使用

row_to_json
。 row_to_jsonb 不存在,但
row_to_json
也会为
JSONB
类型生成所需的结果。


0
投票

那篇审核触发器文章与 JSON 结合起来非常棒。

这是我使用 Postgres 14 和 JSON 完成的实现,因此您不再需要

ROW
了。

  • 我在所有将要审核的表上都有
    updated_by
    created_by
    ,因此我使用它而不是数据库用户来进行
    user_id
  • 我将正在修改的字段的
    id
    提取到它自己的
    record_id
    字段中
  • 我不会在单独的模式中执行此操作
CREATE OR REPLACE
FUNCTION if_modified() RETURNS TRIGGER AS $body$
DECLARE
    v_old_record jsonb;
    v_new_record jsonb;

BEGIN
    IF (TG_OP = 'UPDATE') THEN
        /** {@link https://stackoverflow.com/q/37824289} */
        v_old_record := row_to_json(OLD);
        v_new_record := row_to_json(NEW);

        INSERT INTO audit_logs (action, table_name, user_id, record_id, old_record, new_record)
        VALUES (TG_OP, TG_TABLE_NAME::TEXT, NEW.updated_by, NEW.id::TEXT, v_old_record, v_new_record);

        RETURN NEW;

    ELSIF (TG_OP = 'DELETE') THEN
        v_old_record := row_to_json(OLD);

        INSERT INTO audit_logs (action, table_name, record_id, old_record)
        VALUES (TG_OP, TG_TABLE_NAME::TEXT, OLD.id::TEXT, v_old_record);

        RETURN OLD;

    ELSIF (TG_OP = 'INSERT') THEN
        v_new_record := row_to_json(NEW);

        INSERT INTO audit_logs (action, table_name, user_id, record_id, new_record)
        VALUES (TG_OP, TG_TABLE_NAME::TEXT, NEW.created_by, NEW.id::TEXT, v_new_record);

        RETURN NEW;
    ELSE
        RAISE WARNING '[IF_MODIFIED] - Other action occurred: %, at %', TG_OP, now();

        RETURN NULL;
    END IF;

    EXCEPTION

    WHEN data_exception THEN
        RAISE WARNING '[IF_MODIFIED] - UDF ERROR [DATA EXCEPTION] - SQLSTATE: %, SQLERRM: %', SQLSTATE, SQLERRM;
        RETURN NULL;

    WHEN unique_violation THEN
        RAISE WARNING '[IF_MODIFIED] - UDF ERROR [UNIQUE] - SQLSTATE: %, SQLERRM: %', SQLSTATE, SQLERRM;
        RETURN NULL;

    WHEN OTHERS THEN
        RAISE WARNING '[IF_MODIFIED] - UDF ERROR [OTHER] - SQLSTATE: %, SQLERRM: %', SQLSTATE, SQLERRM;
        RETURN NULL;
END;

$body$
LANGUAGE plpgsql SECURITY DEFINER;

这是我创建的表:

CREATE TABLE audit_logs (
    id serial PRIMARY KEY NOT NULL,
    action TEXT NOT NULL CHECK (action IN ('INSERT', 'DELETE', 'UPDATE')),
    table_name TEXT NOT NULL,
    record_id TEXT,
    old_record jsonb,
    new_record jsonb,
    user_id int,
    created_at timestamp WITH time ZONE NOT NULL DEFAULT current_timestamp
);

ALTER TABLE audit_logs
ADD CONSTRAINT fk_audit_logs_user_id
FOREIGN KEY (user_id)
REFERENCES users(id);

ALTER TABLE audit_logs
ADD CONSTRAINT check_old_record_new_record_not_null
CHECK (
    (old_record IS NOT NULL) OR
    (new_record IS NOT NULL)
);

然后是

users
表上的示例触发器:

CREATE OR REPLACE TRIGGER users_audit
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE PROCEDURE if_modified();
© www.soinside.com 2019 - 2024. All rights reserved.