我正在将 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,我尝试使用配置参数来做类似的事情,即
但是,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
当我在 INNER_PRC 中注释掉 v_dbz := 1/0; 并运行 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()
中的异常并将错误消息作为输出参数返回。