如何识别一个过程在 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

您可以将计数器的递增、检查和重置分解为单独的过程以减少重复性。