未在 raise_application_error 上重新初始化包状态

Lei*_*fel 6 oracle oracle-11g-r2 plsql error-handling

当一个包有状态并且标头改变时,第一次调用会得到一个错误堆栈,如下所示:

ORA-04068: existing state of packages has been discarded
ORA-04061: existing state of package "LRIFFEL.PKG1" has been invalidated
ORA-04065: not executed, altered or dropped package "LRIFFEL.PKG1"
ORA-06508: PL/SQL: could not find program unit being called: "LRIFFEL.PKG1"
ORA-06512: at "LRIFFEL.PKG2", line 7
ORA-06512: at line 1
Run Code Online (Sandbox Code Playgroud)

来自同一会话的后续调用会重新初始化包状态并因此成功运行。有趣的是,当调用者捕获异常并执行 raise_application_error 时,这种行为似乎发生了变化。正如预期的那样, raise_application_error 包含在堆栈中,但出乎意料的是,ORA-04068 没有包含在内。这是一个示例错误堆栈:

ERROR at line 1:
ORA-20001: Failed
ORA-06512: at "LRIFFEL.PKG2", line 7
ORA-04061: existing state of package "LRIFFEL.PKG1" has been invalidated
ORA-04065: not executed, altered or dropped package "LRIFFEL.PKG1"
ORA-06508: PL/SQL: could not find program unit being called: "LRIFFEL.PKG1"
ORA-06512: at line 1
Run Code Online (Sandbox Code Playgroud)

这似乎不仅仅是一个显示问题,因为重复调用该过程会继续出现相同的错误。仅当将 raise_application_error 转换回简单的 raise(或完全消除异常块)时,下一次执行才会包含 ORA-04068 并且接下来的执行成功。

任何人都可以确认和/或解释这种行为吗?这是重现行为的方法:

--Session 1 - Create Objects
create or replace package pkg1 as
   vInteger Integer := 7;
   procedure procA;
end;
/

create or replace 
package body pkg1 as
   procedure procA is
   begin
      DBMS_Output.Put_Line('ProcA');
   end;
end;
/

create or replace 
package pkg2 as
   procedure procB;
end;
/

create or replace 
package body pkg2 as
   procedure procB is
   begin
      pkg1.procA;
   exception
      when others then
         raise;
   end;
end;
/

--Session 2 - Test execution.
execute pkg2.procB;


--Session 1 - Change package header and thus invalidate package state.
create or replace package pkg1 as
   vInteger Integer := 8;
   procedure procA;
end;
/


--Session 2 - Observe failure due to changed package state.
execute pkg2.procB;

--Session 2 - Observe success due to re-initialized package state.
execute pkg2.procB;


--Session 1 - Change Raise to Raise_Application_Error.
create or replace 
package body pkg2 as
   procedure procB is
   begin
      pkg1.procA;
   exception
      when others then
         raise_application_error(-20001,'Failed',True);
   end;
end;
/

--Session 1 - Change package header and thus invalidate package state.
create or replace package pkg1 as
   vInteger Integer := 9;
   procedure procA;
end;
/

--Session 2 - Observe failure due to changed package state.
execute pkg2.procB;


--Session 2 - Observe unexpected continued failures.
execute pkg2.procB;
execute pkg2.procB;
execute pkg2.procB;
execute pkg2.procB;
execute pkg2.procB;
execute pkg2.procB;
execute pkg2.procB;

--Session 1 - Change back to raise.
create or replace 
package body pkg2 as
   procedure procB is
   begin
      pkg1.procA;
   exception
      when others then
         raise;
   end;
end;
/

--Session 2 - Observe failure on the first execution.
execute pkg2.procB;

--Session 2 - Observe success.
execute pkg2.procB;
Run Code Online (Sandbox Code Playgroud)

错误 229349 看起来很相似,只是涉及触发器。

Lei*_*fel 1

Oracle 的回应是,这实际上并不是一个错误,因为应用程序正在捕获 4068 并对其进行处理,从而不允许正常解决错误。他们的解释对我来说似乎不够充分,因为如果包抛出 ORA-01476-除以零(例如),执行 raise_application_error 不会影响 ORA-01476 是否包含在堆栈中。