我有一个函数,它使用 set_config
和 is_local = true
来设置变量。
现在我期望在同一事务中使用带有 current_settings
的变量的 select 语句能够访问该变量,因为文档指出:
set_config(setting_name, new_value, is_local) ... set parameter and return new value
set_config sets the parameter setting_name to new_value. If is_local is true, the new value will only apply to the current transaction.
但这对我来说不起作用,我明白了
ERROR: unrecognized configuration parameter "auth.tenant_id"
SQL state: 42704
有什么我错的地方吗?
这是函数:
CREATE OR REPLACE FUNCTION auth.authorize(IN a_user_id uuid)
RETURNS boolean
LANGUAGE 'plpgsql'
SECURITY DEFINER
AS $BODY$
declare
v_tenant_id uuid;
v_user_role auth.user_role;
begin
select tenant_id, user_role into strict v_tenant_id, v_user_role from auth.authorizations where user_id = a_user_id;
perform set_config('auth.tenant_id', v_tenant_id::text, true);
perform set_config('auth.user_role', v_user_role::text, true);
return true;
exception when no_data_found then return false;
end;
$BODY$;
这是第二个 select 语句失败的事务
begin;
select * from auth.authorize(uuid('180e1b14-21e5-4e66-a9b8-db09139d6278'));
select current_setting('auth.tenant_id') as tenant_id, current_setting('auth.user_role') as user_role;
commit;
最佳答案
set_config(is_local => true)
的效果似乎仅限于您的 BEGIN … EXCEPTION
创建的隐式子事务 block 。
(跳到我对您问题的 TL;DR 解决方案的回答末尾。)
以下是当我执行类似操作但调用 set_config()
时会发生的情况BEGIN … EXCEPTION
之外子事务:
create or replace function set_config_outside_of_begin_except_block(denominator int)
returns bool
language 'plpgsql'
as $body$
begin
perform set_config('my.setting', 'set before subtransaction', true);
begin
perform 10 / denominator;
return true;
exception when division_by_zero then
return false;
end;
end;
$body$;
begin
select current_setting('my.setting') as my_setting;
select set_config_outside_of_begin_except_block(1);
rollback;
CREATE FUNCTION
BEGIN
ERROR: unrecognized configuration parameter "my.setting"
set_config_outside_of_begin_except_block
------------------------------------------
t
(1 row)
my_setting
---------------------------
set before subtransaction
(1 row)
ROLLBACK
移动了set_config()
调用BEGIN … EXCEPTION
之前 block ,my.setting
的新设置已在我们的函数调用之外持续存在。
而且,正如您接下来将看到的,上面的 BEGIN … EXCEPTION
中是否发生异常也没关系。 block ,这是有道理的,因为 set_config()
在输入区 block /子交易之前调用:
begin
select current_setting('my.setting') as my_setting;
rollback;
BEGIN
set_config_outside_of_begin_except_block
------------------------------------------
f
(1 row)
my_setting
---------------------------
set before subtransaction
(1 row)
ROLLBACK
现在,我将稍微修改代码以更接近您的示例,并更接近重现您描述的行为:
create or replace function set_config_in_begin_except_block(denominator int)
returns bool
language 'plpgsql'
as $body$
begin
perform set_config('my.setting', 'set in subtransaction', true);
perform 10 / denominator;
return true;
exception when division_by_zero then
return false;
end;
$body$;
begin;
select set_config_in_begin_except_block(0);
select current_setting('my.setting') as my_setting;
rollback;
CREATE FUNCTION
BEGIN
set_config_in_begin_except_block
----------------------------------
f
(1 row)
my_setting
------------
(1 row)
ROLLBACK
begin
select set_config_in_begin_except_block(1);
select current_setting('my.setting') as my_setting;
rollback;
BEGIN
set_config_in_begin_except_block
----------------------------------
t
(1 row)
my_setting
-----------------------
set in subtransaction
(1 row)
ROLLBACK
请注意,现在,
BEGIN … EXCEPTION
阻止确实会导致my.setting
的更改迷路,- 但前提是确实引发了异常 (
division_by_zero
), - 尽管
division_by_zero
由语句 afterset_config()
触发.
所以,它有点接近您描述的行为,但又不完全一样。您的设置没有持续存在于 BEGIN … EXCEPTION
之外阻止无论是否 no_data_found
异常是否命中。
作为一个小绕道:我偶然发现了你的问题,因为我感兴趣的是我是否可以将这些设置视为与(子)事务堆栈一起展开的堆栈。事实证明我可以:
create or replace function set_config_stacked(denominator int)
returns bool
language 'plpgsql'
as $body$
begin
perform set_config('my.setting', 'set before substraction', true);
begin
perform set_config('my.setting', 'set within subtransaction before division', true);
perform 10 / denominator;
return true;
exception when division_by_zero then
return false;
end;
end;
$body$;
begin;
select set_config_stacked(1);
select current_setting('my.setting') as my_setting;
rollback;
CREATE FUNCTION
BEGIN
set_config_stacked
--------------------
t
(1 row)
my_setting
-------------------------------------------
set within subtransaction before division
(1 row)
ROLLBACK
begin;
select set_config_stacked(0);
select current_setting('my.setting') as my_setting;
rollback;
BEGIN
set_config_stacked
--------------------
f
(1 row)
my_setting
-------------------------
set before substraction
(1 row)
ROLLBACK
所以my.setting
当 BEGIN … EXCEPTION
的子事务时,实际上会恢复到之前的设置。区 block 关闭。
我需要最后一项更改来重现您的函数的行为:
create table tab (id uuid primary key, stuff text);
insert into tab (id, stuff) values (uuid('180e1b14-21e5-4e66-a9b8-db09139d6278'), 'some stuff');
create or replace function set_config_stacked(id$ uuid)
returns bool
language 'plpgsql'
as $body$
declare
rec tab%rowtype;
begin
perform set_config('my.setting', 'set before substraction', true);
begin
perform set_config('my.setting', 'set within subtransaction before SELECT', true);
select * into strict rec from tab where id = id$;
perform set_config('my.setting', 'set within subtransaction after SELECT', true);
return true;
exception when no_data_found then
return false;
end;
end;
$body$;
begin;
select set_config_stacked(uuid('180e1b14-21e5-4e66-a9b8-db09139d6278')); -- exist
select current_setting('my.setting') as my_setting;
rollback;
CREATE TABLE
INSERT 1 0
CREATE FUNCTION
set_config_stacked
--------------------
t
(1 row)
my_setting
----------------------------------------
set within subtransaction after SELECT
(1 row)
ROLLBACK
begin;
select set_config_stacked(uuid('cc7ad0c3-7e3a-49a0-b7d8-7b4093ae0028')); -- doesn't exist
select current_setting('my.setting') as my_setting;
rollback;
BEGIN
set_config_stacked
--------------------
f
(1 row)
my_setting
-------------------------
set before substraction
(1 row)
ROLLBACK
正如您所看到的,您所描述的行为的一个方面我仍然无法重现。此版本set_config_stacked()
围绕 no_data_found
运行异常的行为与基于 division_by_zero
的先前版本相同。异常。
但是,您的示例表明您的设置也不会在 auth.authorize()
之外持续存在。当没有遇到异常时。这让我很困惑,而且我无法重现,至少在 Postgres 14 上是这样。
无论如何,您的问题可以通过将调用移至 set_config()
来解决。低于BEGIN … EXCEPTION … END
堵塞。您仍然希望在该 block 内设置变量,但是执行这些调用和 return true
从新创建的外部BEGIN … END
block (没有 EXCEPTION
)。
关于postgresql - 为什么 postgres set_config 的 is_local = true 不保留整个事务的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71242103/