PostgreSQL - 确定更新是来自触发器还是来自用户

PostgreSQL - Identify if an update is from a trigger or a user

PostgreSQL 13.2 我有一个 table 有 5 个字段:id, val1, val2, val3, update_origin.

第 1 步:创建或更新 'val1' 或 'val2' 时,触发器生成 'val3'。此触发器还使用 'trigger update' 值更新字段 'update_origin'。

第 2 步:用户也可以直接创建或更新 val3,如果是这样,我创建一个触发器,用 'user update' 值更新字段 'update_origin'。

使用此设置,自动触发更新和手动用户更新之间没有区别,因为没有任何内容可以识别更新的来源(第二步总是正确的,对于 PostgreSQL 更新就是更新)。

如何区分触发器更新(步骤 1 的示例)或用户更新(步骤 2 的示例)?

序列

CREATE SEQUENCE demo_id_seq
    INCREMENT 1
    START 1
    MINVALUE 1
    MAXVALUE 2147483647
    CACHE 1;

Table

CREATE TABLE demo
(
    id integer NOT NULL DEFAULT nextval('demo_id_seq'::regclass),    
    val1 character varying COLLATE pg_catalog."default",
    val2 character varying COLLATE pg_catalog."default",
    val3 character varying COLLATE pg_catalog."default",
    update_origin character varying COLLATE pg_catalog."default"
)
TABLESPACE pg_default;

函数 #1

CREATE FUNCTION val3_concat()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
    UPDATE demo
    SET val3 = CONCAT(val1, ' ', val2),
    update_origin = 'trigger update'
    WHERE id = currval('demo_id_seq');
RETURN null;  
END
$BODY$;

触发器 #1

CREATE TRIGGER trigger_val3_concat
    AFTER INSERT OR UPDATE OF val1, val2
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE val3_concat();

函数 #2

CREATE FUNCTION update_origin()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
    IF OLD.val3 <> NEW.val3 THEN
        UPDATE demo
        SET update_origin = 'user update'
        WHERE id = currval('demo_id_seq');
    END IF;
RETURN NEW; 
END
$BODY$;

触发器#2

CREATE TRIGGER trigger_update_origin
    AFTER UPDATE OF val3
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE update_origin();

带有 INSERT INTO demo (val1, val2) VALUES ('first', 'shot'); table 的示例将 return 1, first, shot, first shot, trigger update.

如果我更新 'val1' 或 val2' table 将 return 1, first, shot, first shot, user update 或者我预计只有当我更新 'val3' 并且只有 'val3'.

示例:

CREATE TABLE demo
(
    id serial NOT NULL PRIMARY KEY,    
    val1 varchar,
    val2 varchar,
    val3 varchar,
    update_origin varchar
)
;

CREATE OR REPLACE FUNCTION public.update_origin()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
    IF TG_OP = 'UPDATE' THEN
        IF OLD.val3 IS DISTINCT FROM NEW.val3 THEN
            NEW.update_origin = 'user update';
        ELSE
            NEW.val3 = NEW.val1 || ' ' || NEW.val2;
            NEW.update_origin = 'trigger update';
        END IF;
    ELSEIF TG_OP = 'INSERT' THEN
        IF NEW.val3 IS NOT NULL THEN
            NEW.update_origin = 'user update';
        ELSE
            NEW.val3 = NEW.val1 || ' ' || NEW.val2;
            NEW.update_origin = 'trigger update';
        END IF;
    END IF;
RETURN NEW;
END
$function$
;

CREATE TRIGGER trigger_update_origin
    BEFORE INSERT OR UPDATE
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE update_origin();

insert into demo (val1, val2) values ('1', '2'); 
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  2 | 1    | 2    | 1 2  | trigger update
insert into demo (val1, val2, val3) values ('3', '4', '5');
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  2 | 1    | 2    | 1 2  | trigger update
  3 | 3    | 4    | 5    | user update
update demo set val1 = '6' where id = 2;
UPDATE 1
test_(aklaver)(5432)=> insert into demo (val1, val2, val3) values ('3', '4', '5');
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  3 | 3    | 4    | 5    | user update
  2 | 6    | 2    | 6 2  | trigger update
  4 | 3    | 4    | 5    | user update

将两个功能合二为一,并使用TG_OP区分操作。