如何识别一个过程在 Oracle 中递归执行
How to identify a procedure is executing recursively in Oracle
甲骨文代码:
create or replace PACKAGE BODY P AS
/* TODO enter package declarations (types, exceptions, methods etc) here */
procedure p3 as
begin
p1();
end;
procedure p2 as
begin
dbms_output.put_line('inside p2');
dbms_output.put_line('p2 call stack - '||dbms_utility.format_call_stack);
p3();
end;
procedure p1 as
begin
dbms_output.put_line('inside p1');
dbms_output.put_line('p1 call stack - '||dbms_utility.format_call_stack);
p2();
end;
END P;
过程调用层次结构P1 -> P2 -> P3 -> P1
现在,这将陷入递归调用。
我需要在过程中识别出这样的递归调用并结束调用。
Oracle 12c
中的示例我使用了下面的代码(不确定这是否是实现相同目标的最佳方法。请提出建议)
procedure p1 as
l_depth PLS_INTEGER;
begin
l_depth := UTL_CALL_STACK.dynamic_depth;
dbms_output.put_line('l_depth='||l_depth);
if l_depth > 2 then
FOR i IN 1 .. l_depth LOOP
dbms_output.put_line('UTL_CALL_STACK.subprogram(i)='||UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i)));
IF upper( UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i))) like '%P1%' THEN
dbms_output.put_line('recursive call detected exiting...');
return;
END IF; -- IF upper(UTL_CALL_STACK.subprogram(i)) like '%P1%' THEN
END LOOP;
end if; -- if l_depth > 0 then
dbms_output.put_line('inside p1');
dbms_output.put_line('p1 call stack - '||dbms_utility.format_call_stack);
p2();
end;
但我认为 format_call_stack
中的 subprogram
模块在 Oracle 11g 中不可用。请建议,我怎样才能在 Oracle 11g 中实现相同的目标。谢谢
可能不是最好的,但您可以简单地计算您的过程调用次数:
procedure p3(pCount pls_integer) as
begin
p1(pCount => pCount);
end;
procedure p2(pCount pls_integer) as
begin
p3(pCount => pCount);
end;
procedure p1(pCount pls_integer default 0) as
lCount pls_integer := pCount;
begin
lCount := lCount + 1;
if lCount > 1 then
return;
else
p2(pCount => lCount);
end if;
end;
I am trying to solve the problem on the production environment and adding new param in every proc is not a practical solution in my scenario
您有一个编码错误,我相信您将不得不更改 一些 代码。没有 ALTER PACKAGE
命令或类似的命令告诉 Oracle 在任何过程被重入调用时使其失败。
如果您不喜欢为每个过程添加参数,您可以通过其他方式记录每个过程被调用的次数。一种简单的方法是为每个过程设置一个包级 NUMBER
变量。在每个过程的入口增加适当的变量并在退出时重置它(确保也为异常重置它!)。如果自增后,该值大于1,则已被重入调用,您可以进行相应操作(根据您的要求)。
示例代码:
CREATE OR REPLACE PACKAGE BODY so_pkg AS
-- Variables to hold lock handles for each procedure
p1_ctr NUMBER := 0;
p2_ctr NUMBER := 0;
p3_ctr NUMBER := 0;
procedure p3 as
begin
dbms_output.put_line('In P3!');
p3_ctr := p3_ctr + 1;
if p3_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P3!');
end if;
p1;
p3_ctr := 0;
exception
when others then
p3_ctr := 0;
raise;
end;
procedure p2 as
begin
dbms_output.put_line('In P2!');
p2_ctr := p2_ctr + 1;
if p2_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P2!');
end if;
p3;
p2_ctr := 0;
exception
when others then
p2_ctr := 0;
raise;
end;
procedure p1 as
begin
dbms_output.put_line('In P1!');
p1_ctr := p1_ctr + 1;
if p1_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P1!');
end if;
p2;
p1_ctr := 0;
exception
when others then
p1_ctr := 0;
raise;
end;
END so_pkg;
结果:
In P1!
In P2!
In P3!
In P1!
ORA-20001: Reentrancy detected in P1!
ORA-06512: at "APPS.SO_PKG", line 50
ORA-06512: at line 1
您可以将计数器的递增、检查和重置分解为单独的过程以减少重复性。
甲骨文代码:
create or replace PACKAGE BODY P AS
/* TODO enter package declarations (types, exceptions, methods etc) here */
procedure p3 as
begin
p1();
end;
procedure p2 as
begin
dbms_output.put_line('inside p2');
dbms_output.put_line('p2 call stack - '||dbms_utility.format_call_stack);
p3();
end;
procedure p1 as
begin
dbms_output.put_line('inside p1');
dbms_output.put_line('p1 call stack - '||dbms_utility.format_call_stack);
p2();
end;
END P;
过程调用层次结构P1 -> P2 -> P3 -> P1
现在,这将陷入递归调用。
我需要在过程中识别出这样的递归调用并结束调用。
Oracle 12c
中的示例我使用了下面的代码(不确定这是否是实现相同目标的最佳方法。请提出建议)
procedure p1 as
l_depth PLS_INTEGER;
begin
l_depth := UTL_CALL_STACK.dynamic_depth;
dbms_output.put_line('l_depth='||l_depth);
if l_depth > 2 then
FOR i IN 1 .. l_depth LOOP
dbms_output.put_line('UTL_CALL_STACK.subprogram(i)='||UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i)));
IF upper( UTL_CALL_STACK.concatenate_subprogram(UTL_CALL_STACK.subprogram(i))) like '%P1%' THEN
dbms_output.put_line('recursive call detected exiting...');
return;
END IF; -- IF upper(UTL_CALL_STACK.subprogram(i)) like '%P1%' THEN
END LOOP;
end if; -- if l_depth > 0 then
dbms_output.put_line('inside p1');
dbms_output.put_line('p1 call stack - '||dbms_utility.format_call_stack);
p2();
end;
但我认为 format_call_stack
中的 subprogram
模块在 Oracle 11g 中不可用。请建议,我怎样才能在 Oracle 11g 中实现相同的目标。谢谢
可能不是最好的,但您可以简单地计算您的过程调用次数:
procedure p3(pCount pls_integer) as
begin
p1(pCount => pCount);
end;
procedure p2(pCount pls_integer) as
begin
p3(pCount => pCount);
end;
procedure p1(pCount pls_integer default 0) as
lCount pls_integer := pCount;
begin
lCount := lCount + 1;
if lCount > 1 then
return;
else
p2(pCount => lCount);
end if;
end;
I am trying to solve the problem on the production environment and adding new param in every proc is not a practical solution in my scenario
您有一个编码错误,我相信您将不得不更改 一些 代码。没有 ALTER PACKAGE
命令或类似的命令告诉 Oracle 在任何过程被重入调用时使其失败。
如果您不喜欢为每个过程添加参数,您可以通过其他方式记录每个过程被调用的次数。一种简单的方法是为每个过程设置一个包级 NUMBER
变量。在每个过程的入口增加适当的变量并在退出时重置它(确保也为异常重置它!)。如果自增后,该值大于1,则已被重入调用,您可以进行相应操作(根据您的要求)。
示例代码:
CREATE OR REPLACE PACKAGE BODY so_pkg AS
-- Variables to hold lock handles for each procedure
p1_ctr NUMBER := 0;
p2_ctr NUMBER := 0;
p3_ctr NUMBER := 0;
procedure p3 as
begin
dbms_output.put_line('In P3!');
p3_ctr := p3_ctr + 1;
if p3_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P3!');
end if;
p1;
p3_ctr := 0;
exception
when others then
p3_ctr := 0;
raise;
end;
procedure p2 as
begin
dbms_output.put_line('In P2!');
p2_ctr := p2_ctr + 1;
if p2_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P2!');
end if;
p3;
p2_ctr := 0;
exception
when others then
p2_ctr := 0;
raise;
end;
procedure p1 as
begin
dbms_output.put_line('In P1!');
p1_ctr := p1_ctr + 1;
if p1_ctr > 1 THEN
raise_application_error(-20001,'Reentrancy detected in P1!');
end if;
p2;
p1_ctr := 0;
exception
when others then
p1_ctr := 0;
raise;
end;
END so_pkg;
结果:
In P1! In P2! In P3! In P1! ORA-20001: Reentrancy detected in P1! ORA-06512: at "APPS.SO_PKG", line 50 ORA-06512: at line 1
您可以将计数器的递增、检查和重置分解为单独的过程以减少重复性。