确定 PL/SQL 存储过程的调用层次结构

4

我正在处理一些旧的PL/SQL代码,并且希望像在Eclipse和Java代码中一样显示调用层次结构。

例如,如果我有以下代码:

create or replace package body pkgA as 
  procedure foobar is begin
    lambda(1);
  end;

  procedure lambda(a NUMBER) is begin
    pkgB.test();   
  end;
end pkgA;
/
create or replace package body pkgB as 
  procedure test is begin
    select 1 from dual;
  end;
end pkgB;
/

我想要这棵树:
  1. pkgB.test
    1. pkgA.lambda
      1. pkgA.foobar
注意:我使用的是Toad 9,但我没有看到这样的功能(除非我需要查找类似于外键引用树之类的东西)。
此外,我更多地寻求静态分析而不是动态分析,或者是涉及执行代码的一些东西。

PL/SQL 很可能会错过这样的工具。请尝试使用 PL/SQL 分层分析器。不过你需要执行代码才行。 - user272735
1
PL/Scope 可以做到这一点。我现在没有时间创建一个具体的例子,也许有人可以。 - Jon Heller
2
有关 PL/Scope 的信息,请阅读此文章:http://www.oracle.com/au/products/database/o50plsql-165471.html。 - Bob Jarvis - Слава Україні
是的,这对于调试很有用,但这个意味着执行代码,而不是静态分析(我需要的)。 - NoDataFound
更新了@BobJarvis-ReinstateMonica的链接,网址为https://blogs.oracle.com/oraclemagazine/zoom-in-on-your-code - Mark Stewart
显示剩余2条评论
3个回答

1
我认为更好的包调用方式是:dbms_utility.format_error_backtrace。
因此,您可以使用DBMS_OUTPUT.PUT_LINE(dbms_utility.format_error_backtrace)。我经常将它们放在我的异常处理程序中进行测试(到了我有一个代码模板可以放置它的地步)。我还输出指令:
所以它返回如下结果:
EXCEPTION IN aeo_misc_tools.cursor_to_listV2 - -900: ORA-00900: invalid SQL statement
EXCEPTION IN my test script - -900: ORA-00900: invalid SQL statement
Error stack at top level:
ORA-06512: at "AEO.AEO_MISC_TOOLS", line 805
ORA-06512: at line 8

它不会按照您指定的树形格式对错误进行格式化,但它可以完成工作,以便您可以找到正确行上的错误。

这不是一个完整的解决方案,如果能够完全自动化地完成它将是非常好的。想象一下,你面对着一个庞大的企业遗留代码库,其中一个包含60k行plsql代码。 - Alexander.Iljushkin
1
看起来 UTL_CALL_STACK 更加灵活,这里有一个很好的概述:https://oracle-base.com/articles/12c/utl-call-stack-12cr1 - Mark Stewart
1
@MarkStewart - 感谢提供链接,但实际上我从那篇文章中引用的UTL_CALL_STACK例程中看到的有用输出比DBMS_UTILITY.FORMAT_ERROR_BACKTRACE还要少。文章示例表明您应该看到所有调用级别,但我只看到了第一个和最后一个。其他示例给出了类似的有限结果。我确认了我们的版本,但不知道为什么我看到的较少。 - StewS2

0

您可以使用Visual-Expert的调用层次结构,该工具分析PL代码并显示调用函数。

Called hierarchy of PL code


0

这可能不是最好的解决方案,但我已经通过递归使用PL/Scope找到了答案。希望它能帮助有需要的人。:)

create or replace procedure getAllChildCall ( definitionName in char, packageName in char, callLevel in number )
as
begin
    for rec in (
        with package_tree as (
            select * from all_identifiers
            where object_name = packageName and object_type = 'PACKAGE BODY'
        )
        select distinct 
            name, signature, type, line,
            usage, usage_id, usage_context_id 
        from package_tree
        start with (name = definitionName and usage = 'DEFINITION')
        connect by prior usage_id = usage_context_id
    ) loop
        if rec.usage = 'CALL' and rec.name not in (
        'DEBUG', 'SQLERRM', 'COMMITRECORD', 'ISNOTZERO', 'NVL', 'TRUNC', 'ROUND') then
            dbms_output.put_line(LPAD(' ', 2*callLevel, ' ') || rec.name);
            getallchildcall(rec.name, packageName, callLevel+1);
        end if;
    end loop;
end;
/

set serveroutput on

declare
    callLevel number;
begin
callLevel := 1;

dbms_output.put_line('start traverse the package child call graph...');
getAllChildCall(upper('YOUR_PROCEDURE_NAME'), upper('YOUR_PACKAGE_NAME'), callLevel);

end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接