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

Posted

技术标签:

【中文标题】确定 PL/SQL 过程的调用层次结构【英文标题】:Determining call hierarchy of PL/SQL procedure 【发布时间】:2015-02-06 20:58:02 【问题描述】:

我正在处理一些旧的 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;
/

我想要这棵树:

    pkgB.test
      pkgA.lambda
        pkgA.foobar

注意:我使用的是 Toad 9,但我没有看到这样的功能(好吧,除非我需要寻找外键的引用树之类的东西)。

此外,我更多的是寻找静态分析而不是动态分析,或者暗示我执行代码的东西。

【问题讨论】:

PL/SQL 很可能会错过这样的工具。请尝试PL/SQL Hierarchical Profiler。你必须执行代码。 PL/Scope 可以做到这一点。我现在没有时间创建一个具体的例子,也许其他人可以。 关于 PL/Scope 的信息 read this article. DBMS_DEBUG.PRINT_BACKTRACE 您可以在需要堆栈跟踪的程序单元中使用dbms_utility.format_Call_stack 【参考方案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 代码组成。 貌似UTL_CALL_STACK灵活一点,oracle-base.com/articles/12c/utl-call-stack-12cr1有很好的概述 @MarkStewart - 感谢您的链接,但实际上我从该文章中引用的 UTL_CALL_STACK 例程中看到的有用输出比从 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 中看到的要少。文章示例显示您应该看到所有调用级别,但我只看到第一个和最后一个。其他例子给了我类似的,有限的结果。我确认了我们的版本,但不知道为什么我看到的更少。【参考方案2】:

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

【讨论】:

【参考方案3】:

这可能不是最好的解决方案,但我已经通过递归使用 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;

【讨论】:

以上是关于确定 PL/SQL 过程的调用层次结构的主要内容,如果未能解决你的问题,请参考以下文章

如何确定通用 PL/SQL 过程的参数(执行 group by 子句)?

oracle SQL语句中怎么样调用存储过程

将同一层次结构级别的两个节点与 PL/SQL 关联

带有 PL SQL 表类型参数的 Oracle 存储过程的 JDBC 调用

ORACLE PL/SQL:以最大执行时间调用外部 Oracle PL/SQL 过程

ORA-29270:从pl / sql过程调用webservice时,有太多打开的HTTP请求