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
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