postgres触发器添加条件到审计功能以用数据库用户填充用户

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

我使用 postgresql 11

我有一个表employee_audit,我运行一个触发器,它工作成功。

这是代码:

CREATE TABLE audit_log.employee_audit
(
    operation char(15) NOT NULL,
    stamp timestamp NOT NULL,
    userid text ,
    id character varying(15) NOT NULL,
  tel character varying(15),
  fax character varying(15),
  mail character varying(100)
  created_by character varying(50),
  created_date timestamp without time zone,
  last_modified_by character varying(50),
  last_modified_date timestamp without time zone
   
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE audit_log.employee_audit
    OWNER to postgres;
    
------------------------------------- Fin création table employee_audit

------- Create the function to insert into the log table
CREATE OR REPLACE FUNCTION audit_log.process_employee_audit() RETURNS TRIGGER AS $employee_audit$
    BEGIN
        --
        -- Create a row in emp_audit to reflect the operation performed on emp,
        -- make use of the special variable TG_OP to work out the operation.
        --
        IF (TG_OP = 'DELETE') THEN
            INSERT INTO audit_log.employee_audit SELECT 'DELETE', now(), NEW.last_modified_by, OLD.*;
            RETURN OLD;
        ELSIF (TG_OP = 'UPDATE') THEN
          IF (TG_WHEN='AFTER') THEN
            INSERT INTO audit_log.employee_audit SELECT 'AFTER UPDATE', now(), NEW.last_modified_by, NEW.*;
          ELSIF (TG_WHEN='BEFORE') THEN
            INSERT INTO audit_log.employee_audit SELECT 'BEFORE UPDATE', now(), OLD.last_modified_by, OLD.*;
          END IF;
          RETURN NEW;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO audit_log.employee_audit SELECT 'INSERT', now(), NEW.created_by, NEW.*;
            RETURN NEW;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$employee_audit$ LANGUAGE plpgsql;

-------- Create AFTER trigger on the employee table
CREATE TRIGGER employee_audit
AFTER INSERT OR UPDATE OR DELETE ON public.employee
    FOR EACH ROW EXECUTE PROCEDURE audit_log.process_employee_audit();

-------- Crete Before Trigger on the employee Table
CREATE TRIGGER employee_audit_before
Before UPDATE ON public.employee
    FOR EACH ROW EXECUTE PROCEDURE audit_log.process_employee_audit();
    
    

我在更新中有一个用例,特别是在“更新后

userid 将由 NEW.last_modified_by

填充

在我的更新功能应用程序中,我填写了此列last_modified_by 由连接的用户。

这是代码:

public UnitDTO updateUnit(UnitDTO unitDTO) {

    User user = userService.getUserWithAuthorities().get();
    
    Unit unit = unitMapper.toEntity(unitDTO);
    unit.setLastModifiedBy(user.getEmployee().getId());
    unit.setLastModifiedDate(LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()));
    
    unit = unitRepository.save(unit);


    return newUnitDTO;
}

并且在表employee_audit中,列userid包含正确的数据,即 应用程序的连接用户(进行更新的人)。

我现在的问题是,当我直接连接到数据库并更新 employee 表中的一些数据时

employee_audit中我发现列userid包含last_modified_by的数据 已经插入的列。

正确的值应该是数据库的用户,因为我进行了更新 直接在数据库中 或者应该是空的

我认为我应该在“更新后”情况下的触发器中设置一些条件 (所以改变NEW.last_modified_by

postgresql triggers
1个回答
0
投票

您记录谁在何时做了什么的方法是错误的。您不能依赖您的应用程序执行此操作,正是因为,就像您现在注意到的那样,您不能保证每个查询都会由它生成。

首先,您需要创建您的应用程序,以便每个应用程序用户都是数据库中的登录名。 Postgres 有一个

session_user
变量返回该登录名(只有超级用户可以更改其值)。如果您的应用程序使用通用登录名+存储在您创建的表中的用户记录进行连接,则需要更改它。

在纠正此问题的同时,您还可以使审核系统更加通用:

第 1 步:创建通用表和触发器函数来处理您可能想要的所有审核:

CREATE TABLE audit_template
(
    created_by name NOT NULL,
    created_date timestamp without time zone NOT NULL,
    last_modified_by name NOT NULL,
    last_modified_date timestamp without time zone  NOT NULL
);

CREATE OR REPLACE FUNCTION audit_set_user()
RETURNS TRIGGER
LANGUAGE 'plpgsql'
VOLATILE NOT LEAKPROOF
AS $BODY$
    BEGIN
        IF TG_OP = 'INSERT' THEN
            NEW.created_by := SESSION_USER;
            NEW.created_date := current_timestamp;
        END IF;
        NEW.last_modified_by := SESSION_USER;
        NEW.last_modified_date := current_timestamp;
        RETURN NEW;
    END;
$BODY$;

请注意,我使用

NOT NULL
声明了表,并且没有列的默认值,因为触发器将负责填充缺失的位。

第 2 步:创建
employee
表和关联的
BEFORE
触发器:

员工继承

audit_template
:

CREATE TABLE employee
(
    stamp timestamp NOT NULL,
    userid text ,
    id character varying(15) NOT NULL,
    tel character varying(15),
    fax character varying(15),
    mail character varying(100)
) INHERITS (audit_template);

CREATE TRIGGER employee_fill_audit
BEFORE INSERT OR UPDATE OR DELETE ON employee
FOR EACH ROW EXECUTE PROCEDURE audit_set_user();

在此阶段,触发器将确保审核数据正确填写到

employee
表中。
顺便说一句,我没有更改列的定义,但感觉缺少一些独特的约束(例如,在
id
mail
等)。

第3步:创建员工审核表来记录更改+关联的触发器函数和触发器。

CREATE TABLE employee_audit
(
    stamp timestamp NOT NULL,
    userid text ,
    id character varying(15) NOT NULL,
    tel character varying(15),
    fax character varying(15),
    mail character varying(100),
    OP TEXT NOT NULL
) INHERITS (audit_template);

CREATE OR REPLACE FUNCTION employee_audit_log()
    RETURNS trigger
    LANGUAGE 'plpgsql'
AS $BODY$
    BEGIN
        If TG_OP = 'INSERT' THEN
            INSERT INTO employee_audit SELECT New.*, TG_OP;
        ELSE
            INSERT INTO employee_audit SELECT Old.*, TG_OP;
        END IF;
        RETURN NEW;
    END;
$BODY$;


CREATE TRIGGER employee_log_to_audit
AFTER INSERT OR UPDATE OR DELETE ON employee
FOR EACH ROW EXECUTE PROCEDURE employee_audit_log();

还有一种创建

employee_audit
的替代方法,可以通过这种方式完成(以及对触发器的一些更改):

CREATE TABLE employee_audit
(
    OP TEXT NOT NULL
) INHERITS (employee);

不过我不太喜欢它,因为它会导致

SELECT * FROM employee
返回两个表的内容。要从
employee
获取没有
employee_audit
的内容,您必须使用查询:

SELECT * FROM ONLY employee

第 4 步:观察其工作情况。

我让您在以下每个查询之后执行

SELECT * FROM employee
SELECT * FROM employee_audit

INSERT INTO employee(stamp, id) VALUES (now(), 'Some id');

UPDATE employee SET id = 'Some other id';

DELETE FROM employee;
© www.soinside.com 2019 - 2024. All rights reserved.