提问人:andrewtindle 提问时间:10/26/2023 更新时间:10/26/2023 访问量:27
PostgreSQL - 引发异常时set_config参数未在存储过程之间持久化
PostgreSQL - set_config parameter is not being persisted between stored procedures when exception raised
问:
我正在将 Oracle 解决方案转换为 PostgreSQL,其中 Oracle 解决方案利用 PL/SQL 全局变量在内部过程中发生异常时分配错误消息,以便可以在外部过程中引用它们,例如(非常基本):
BEGIN
DBMS_OUTPUT.PUT_LINE(1/0);
EXCEPTION
WHEN OTHERS THEN
gv_error_text := 'Failure during INNER_PRC. Oracle Error: '||SUBSTR(SQLERRM,1,512);
RAISE ge_app_exception;
END;
PROCEDURE outer_prc IS
BEGIN
inner_prc;
EXCEPTION
WHEN ge_app_exception THEN
RAISE_APPLICATION_ERROR(-20000,gv_error_text);
END;
其中 gv_error_text 和 ge_app_exception 在 PL/SQL 包的上下文中是全局的。
对于PostgreSQL,我正在尝试使用配置参数来做类似的事情,即
- 在 INNER_PRC 的 EXCEPTION 块中设置 config 参数,使用 set_config
- 使用 current_setting 读取 OUTER_PRC 的 EXCEPTION 块中的 config 参数
但是,OUTER_PRC没有看到新值,可能是由于 WHEN OTHERS 块中的设置被回滚
为了使用一个非常基本的示例来演示这一点,我在 postgresql.conf 中分配了以下参数:test.test_config = 'ORIGINAL_DEFAULT_VALUE' 并创建以下两个存储过程:
DECLARE
v_test_config VARCHAR(30);
v_dbz INTEGER;
BEGIN
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'INNER_PRC: Starting test.test_config value: %',v_test_config;
BEGIN
PERFORM set_config('test.test_config','NEW_VALUE',false);
END;
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'INNER_PRC: Ending test.test_config value: %',v_test_config;
--Raise Error
v_dbz := 1/0;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'INNER_PRC WHEN OTHERS: test.test_config value: %',v_test_config;
PERFORM set_config('test.test_config','EXCEPTION_VALUE',false);
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'INNER_PRC WHEN OTHERS: Ending test.test_config value: %',v_test_config;
RAISE EXCEPTION USING ERRCODE = 'T2000';
END; $$
LANGUAGE PLPGSQL
CREATE OR REPLACE PROCEDURE outer_prc () AS $$
DECLARE
v_test_config VARCHAR(30);
BEGIN
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'OUTER_PRC: Starting test.test_config value: %',v_test_config;
CALL inner_prc();
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'OUTER_PRC: Ending test.test_config value: %',v_test_config;
EXCEPTION
WHEN OTHERS THEN
v_test_config := current_setting('test.test_config');
RAISE NOTICE 'OUTER_PRC WHEN OTHERS: Ending test.test_config value: %',v_test_config;
END; $$
LANGUAGE PLPGSQL
当我注释掉v_dbz:= 1/0;时,INNER_PRC并运行OUTER_PRC,输出符合预期:
psql:test.OUTER_PRC.sql:5: NOTICE: OUTER_PRC: Starting test.test_config value: ORIGINAL_DEFAULT_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC: Starting test.test_config value: ORIGINAL_DEFAULT_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC: Ending test.test_config value: NEW_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: OUTER_PRC: Ending test.test_config value: NEW_VALUE
但是一旦我删除了注释,即触发错误,输出就变成了:
psql:test.OUTER_PRC.sql:5: NOTICE: OUTER_PRC: Starting test.test_config value: ORIGINAL_DEFAULT_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC: Starting test.test_config value: ORIGINAL_DEFAULT_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC: Ending test.test_config value: NEW_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC WHEN OTHERS: test.test_config value: NEW_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: INNER_PRC WHEN OTHERS: Ending test.test_config value: EXCEPTION_VALUE
psql:test.OUTER_PRC.sql:5: NOTICE: OUTER_PRC WHEN OTHERS: Ending test.test_config value: ORIGINAL_DEFAULT_VALUE
即test.test_config在 INNER_PRC 的 EXCEPTION 块中被分配了新值,但在 OUTER_PRC 的 EXCEPTION 块中,它被检索为其默认值。
关于如何克服这个问题的任何帮助/建议都是可能的。
或者,如果有其他解决方案可以在不使用配置参数的情况下实现此行为,请告诉我。
谢谢 安德鲁
答:
与其他所有内容一样,设置占位符参数将随子事务一起回滚。请看这段代码:
SET my.param = 'before the call';
BEGIN
CALL inner_proc();
EXCEPTION
WHEN raise_exception THEN
RAISE EXCEPTION '%', current_setting('my.param');
END;
无论发生什么,当我们进入异常处理程序时,它都会被回滚,所以异常总是说“在调用之前”。inner_proc()
您将不得不重写代码。一种方法是捕获异常并将错误消息作为输出参数返回。inner_proc()
评论