【问题标题】:postgresql function and trigger execute select current_setting into integer variable causes errorpostgresql 函数和触发器执行 select current_setting into integer 变量会导致错误
【发布时间】:2021-11-03 14:00:51
【问题描述】:

Postgresql 9.6.x 我在使用 postgresql 函数时遇到错误,我正在记录每个表修改的日志。这一切都很好,直到我添加了这个功能,我正在使用 postgresql 的 current_setting 功能记录当前用户 ID。我将当前用户设置在后台交易中,如下所示:

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

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

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

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

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

我不明白为什么在这个设置中它会对 null 值感到不安。但似乎仅限于这种类型的触发功能。下面基本上是我正在使用的功能

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;

有什么想法吗?

【问题讨论】:

  • 我无法重现;它必须是另一个失败的语句。不要将动态 SQL 与 EXECUTE 一起使用,静态 SQL 会做得更好。

标签: postgresql plpgsql


【解决方案1】:

myvars.active_user_id 这样的GUC(全局用户设置)变量在内部不能为空。它包含文本或空文本。这些变量不能存储 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 $1 = '' then NULL else $1 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

【讨论】:

  • 很好的答案。感谢您抽出宝贵时间提供。似乎应该将可空函数内置到 postgresql 中。
  • 该函数在 Postgres 中。请参阅NULLIF的文档
  • @Belayer - 是的,是的 - 谢谢你的信息
猜你喜欢
  • 1970-01-01
  • 2017-09-13
  • 2011-03-05
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多