PostgreSQL 在触发器函数中动态修改新记录中的字段

2023-12-05

我有一个包含 ID 和用户名(以及其他详细信息)的用户表,以及引用该表的其他几个表,其中包含各种列名称(CONSTRAINT some_name FOREIGN KEY (columnname) REFERENCES "user" (userid))。我需要做的是将用户名添加到引用表中(准备删除整个用户表)。这当然可以通过一个简单的方法轻松完成ALTER TABLE and UPDATE,并且使这些触发器保持最新状态也(相当)容易。但触发功能让我有些烦恼。我可以为每个表使用单独的函数,但这似乎是多余的,因此我为此创建了一个通用函数:

CREATE OR REPLACE FUNCTION public.add_username() RETURNS trigger AS
$BODY$
  DECLARE
    sourcefield text;
    targetfield text;
    username text;
    existing text;
  BEGIN
    IF (TG_NARGS != 2) THEN
      RAISE EXCEPTION 'Need source field and target field parameters';
    END IF;
    sourcefield = TG_ARGV[0];
    targetfield = TG_ARGV[1];
    EXECUTE 'SELECT username FROM "user" WHERE userid = ($1).' || sourcefield INTO username USING NEW;
    EXECUTE format('SELECT ($1).%I', targetfield) INTO existing USING NEW;
    IF ((TG_OP = 'INSERT' AND existing IS NULL) OR (TG_OP = 'UPDATE' AND (existing IS NULL OR username != existing))) THEN
      CASE targetfield
        WHEN 'username' THEN
          NEW.username := username;
        WHEN 'modifiername' THEN
          NEW.modifiername := username;
        WHEN 'creatorname' THEN
          NEW.creatorname := username;
        .....
      END CASE;
    END IF;
    RETURN NEW;
  END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

并使用触发函数:

CREATE TRIGGER some_trigger_name BEFORE UPDATE OR INSERT ON my_schema.my_table FOR EACH ROW EXECUTE PROCEDURE public.add_username('userid', 'username');

其工作方式是触发函数接收原始源字段名称(例如userid) 和目标字段名称 (username)通过 TG_ARGV。然后使用这些来填充(可能)缺失的信息。所有这些都足够好,但我怎样才能摆脱它CASE-混乱?有没有办法动态修改里面的值NEW当我事先不知道字段名称(或者更确切地说它可以是很多东西)时记录?它是在targetfield参数,但显然NEW.targetfield不起作用,也没有类似的东西NEW[targetfield](例如 JavaScript)。

有什么想法可以实现这一点吗?除了使用 PL/Python 等..


没有简单的基于 plpgsql 的解决方案。一些可能的解决方案:

  1. Using hstore扩大。


CREATE TYPE footype AS (a int, b int, c int);

postgres=# select row(10,20,30);
    row     
------------
 (10,20,30)
(1 row)

postgres=# select row(10,20,30)::footype #= 'b=>100';
  ?column?   
-------------
 (10,100,30)
(1 row)
  

hstore基于函数可以非常简单:



create or replace function update_fields(r anyelement,
                                         variadic changes text[])
returns anyelement as $$
select $1 #= hstore($2);
$$ language sql;

postgres=# select * 
             from update_fields(row(10,20,30)::footype, 
                                'b', '1000', 'c', '800');
 a  |  b   |  c  
----+------+-----
 10 | 1000 | 800
(1 row)
  
  1. 几年前我写了一个扩展PL工具箱。有一个功能record_set_fields:



pavel=# select * from pst.record_expand(pst.record_set_fields(row(10,20),'f1',33));
 name | value |   typ   
------+-------+---------
 f1   | 33    | integer
 f2   | 20    | integer
(2 rows)
  

也许您可以找到一些仅基于 plpgsql 的解决方案,这些解决方案基于系统表和数组的一些技巧,例如this,但我不能建议它。它的可读性太差,对于非高级用户来说只是黑魔法。hstore很简单,而且几乎无处不在,所以它应该是首选方式。

在 PostgreSQL 9.4(也许是 9.3)上,你可以尝试使用 JSON 操作进行黑魔法:



postgres=# select json_populate_record(NULL::footype, jo) 
              from (select json_object(array_agg(key),
                                       array_agg(case key when 'b' 
                                                          then 1000::text
                                                          else value 
                                                 end)) jo
       from json_each_text(row_to_json(row(10,20,30)::footype))) x;
 json_populate_record 
----------------------
 (10,1000,30)
(1 row)
  

所以我可以编写函数:



CREATE OR REPLACE FUNCTION public.update_field(r anyelement, 
                                               fn text, val text, 
                                               OUT result anyelement)
 RETURNS anyelement
 LANGUAGE plpgsql
AS $function$
declare jo json;
begin
  jo := (select json_object(array_agg(key), 
                            array_agg(case key when 'b' then val
                                               else value end)) 
            from json_each_text(row_to_json(r)));
  result := json_populate_record(r, jo);
end;
$function$

postgres=# select * from update_field(row(10,20,30)::footype, 'b', '1000');
 a  |  b   | c  
----+------+----
 10 | 1000 | 30
(1 row)
  

基于 JSON 的函数应该不会太快。hstore应该更快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PostgreSQL 在触发器函数中动态修改新记录中的字段 的相关文章

随机推荐