postgresql 函数和触发器将 select current_setting 执行到整数变量中导致错误

postgresql function and trigger execute select current_setting into integer variable causes error

PostgreSQL 9.6.x 我在 postgresql 函数中遇到错误,我在其中记录每个 table 修改的日志。这一切都很好,直到我添加了这个功能,我使用 postgresql 的 current_setting 功能记录当前用户 ID。我像这样在后台设置当前用户的交易:

 select  set_config('myvars.active_user_id', '2123', true)

所有这些功能都可以正常工作,除非未设置用户。当后端系统查询更新 table 时会发生这种情况,在这种情况下设置 'myvars.active_user_id' 为空。
我希望它在未设置时为空。日志中的用户 ID 字段可以为空。 它似乎试图将 null 转换为空字符串并将其放入它不喜欢的整数变量中。
这似乎是某种特定于带有触发器的函数的奇怪问题。我一定是做错了什么,因为据我所知,通过 select... 分配一个空值到 " 是没有问题的。 我在那种情况下得到的错误是这样的:

PSQLException: ERROR: invalid input syntax for integer: ""

我添加了跟踪语句,它在这一行:

EXECUTE 'select current_setting(''myvars.active_user_id'', true)  ' into log_user_id;

我不明白为什么在此设置中它会对空值感到不安。不过好像仅限于这种触发功能。下面基本上是我正在使用的功能

CREATE OR REPLACE FUNCTION update_log() RETURNS TRIGGER AS $update_log$
DECLARE
    logid int;
   log_user_id int;
BEGIN
EXECUTE 'select current_setting(''myvars.active_user_id'', true)  ' into log_user_id;

  IF (TG_OP='DELETE') THEN
            EXECUTE 'select nextval(''seq_log'') ' into logid;
--            INSERT INTO log ....
            RETURN NULL;
  ELSIF (TG_OP='INSERT') THEN
            EXECUTE 'select nextval(''seq_log'') ' into logid;
--            INSERT INTO log ....
            RETURN NEW;

  ELSIF (TG_OP='UPDATE') THEN
--            INSERT INTO log ....
        END IF;
  END IF;
        RETURN NULL; 
    END;
$log$ LANGUAGE plpgsql;

有什么想法吗?

GUC(全局用户设置)变量,如您的 myvars.active_user_id,在内部不可为空。它包含文本或空文本。这些变量不能存储 NULL。所以当你存储 NULL 时,空字符串被存储,这个空字符串从函数 current_setting.

返回

在 Postgres(以及任何没有 Oracle 的数据库)中,NULL 不是空字符串,空字符串也不是 NULL。

所以这个错误是预料之中的:

postgres=# do $$
declare x int;
begin
  perform set_config('x.xx', null, false);
  execute $_$ select current_setting('x.xx', true) $_$ into x;
end;
$$;
ERROR:  invalid input syntax for integer: ""
CONTEXT:  PL/pgSQL function inline_code_block line 5 at EXECUTE

需要先检查结果,将空字符串替换为NULL:

create or replace function nullable(anyelement)
returns anyelement as $$
  select case when  = '' then NULL else  end;
$$ language sql;

do $$
declare x int;
begin
  perform set_config('x.xx', null, false);
  execute $_$ select nullable(current_setting('x.xx', true)) $_$ into x;
end;
$$;
DO

@Laurenz Albe 在您的评论中说得很对。仅在必要时使用动态 SQL(execute 命令)。不是这种情况。所以你的代码应该是这样的:

do $$
declare x int;
begin
  perform set_config('x.xx', null, false);
  x := nullable(current_setting('x.xx', true)); 
end;
$$;
DO

注意:有内置函数 nullif,因此您的代码看起来像(当然,内置功能应该是首选):

do $$
declare x int;
begin
  perform set_config('x.xx', null, false);
  x := nullif(current_setting('x.xx', true), ''); 
end;
$$;
DO