PostgreSQL - 引发异常时set_config参数未在存储过程之间持久化

PostgreSQL - set_config parameter is not being persisted between stored procedures when exception raised

提问人:andrewtindle 提问时间:10/26/2023 更新时间:10/26/2023 访问量:27

问:

我正在将 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_textge_app_exception 在 PL/SQL 包的上下文中是全局的。

对于PostgreSQL,我正在尝试使用配置参数来做类似的事情,即

  1. 在 INNER_PRC 的 EXCEPTION 块中设置 config 参数,使用 set_config
  2. 使用 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 块中,它被检索为其默认值。

关于如何克服这个问题的任何帮助/建议都是可能的。

或者,如果有其他解决方案可以在不使用配置参数的情况下实现此行为,请告诉我。

谢谢 安德鲁

PostgreSQL 异常 存储过程 回滚

评论


答:

0赞 Laurenz Albe 10/26/2023 #1

与其他所有内容一样,设置占位符参数将随子事务一起回滚。请看这段代码:

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

评论

0赞 andrewtindle 10/26/2023
谢谢劳伦斯。我真的希望不必那样做:-(