我尝试按照以下说明进行操作:
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) 不存在
文档中指出
表9-42显示了可用于创建json的函数 和 jsonb 值。 (jsonb 没有等效的函数, row_to_json 和 array_to_json 函数。但是,to_jsonb function 提供与这些函数大致相同的功能 会的。)
因此必须使用
row_to_json
。 row_to_jsonb 不存在,但 row_to_json
也会为 JSONB
类型生成所需的结果。
那篇审核触发器文章与 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();