How to find dependencies inside an oracle package?

后端 未结 1 1478
忘了有多久
忘了有多久 2020-12-18 14:38

My question is how to find inside oracle package dependencies by SQL query or any other internal/external tool. Is it even possible or should I just go trough the code and f

1条回答
  •  有刺的猬
    2020-12-18 14:55

    You can get the calls to procedures within a package with PL/Scope, starting with:

    alter session set plscope_settings = 'IDENTIFIERS:ALL';
    

    If you then recompile your package, here using a simple outline based on your description:

    create or replace package p42 as
      procedure a;
      procedure b;
      procedure c;
      procedure d;
      function f return number;
    end p42;
    /
    
    create or replace package body p42 as
    
      procedure a is
      begin
        b;
        c;
      end a;
    
      procedure b is
        n number;
      begin
        n := f;
      end b;
    
      procedure c is
        n number;
      begin
        n := f;
      end c;
    
      procedure d is
      begin
        null;
      end d;
    
      function f return number is
      begin
        return 42;
      end f;
    end p42;
    /
    

    then you can see the various references in the user_identifiers view. Using the example from the documentation to get an outline:

    WITH v AS (
      SELECT    Line,
                Col,
                INITCAP(NAME) Name,
                LOWER(TYPE)   Type,
                LOWER(USAGE)  Usage,
                USAGE_ID,
                USAGE_CONTEXT_ID
        FROM USER_IDENTIFIERS
          WHERE Object_Name = 'P42'
            AND Object_Type = 'PACKAGE BODY'
    )
    SELECT RPAD(LPAD(' ', 2*(Level-1)) ||
                     Name, 20, '.')||' '||
                     RPAD(Type, 20)||
                     RPAD(Usage, 20)
                     IDENTIFIER_USAGE_CONTEXTS
      FROM v
      START WITH USAGE_CONTEXT_ID = 0
      CONNECT BY PRIOR USAGE_ID = USAGE_CONTEXT_ID
      ORDER SIBLINGS BY Line, Col
    /
    
    IDENTIFIER_USAGE_CONTEXTS                                   
    -------------------------------------------------------------
    P42................. package             definition          
      A................. procedure           definition          
        B............... procedure           call                
        C............... procedure           call                
      B................. procedure           definition          
        N............... variable            declaration         
          Number........ number datatype     reference           
        N............... variable            assignment          
          F............. function            call                
      C................. procedure           definition          
        N............... variable            declaration         
          Number........ number datatype     reference           
        N............... variable            assignment          
          F............. function            call                
      D................. procedure           definition          
      F................. function            definition          
        Number.......... number datatype     reference           
    

    To get something closer to what you want you still need a hierarchical query to find the name of each procedure/function that calls another, since you aren't interested in (for example) the assignment steps, just where those happened.

    As a starting point you could do:

    select *
    from (
      select name, type, connect_by_root(name) as root_name,
        connect_by_root(type) as root_type, connect_by_isleaf as isleaf
      from user_identifiers
      start with object_type = 'PACKAGE BODY'
      and object_name = 'P42'
      and type in ('FUNCTION', 'PROCEDURE')
      and usage = 'DEFINITION'
      connect by object_type = prior object_type
      and object_name = prior object_name
      and usage_context_id = prior usage_id
    )
    where type in ('FUNCTION', 'PROCEDURE');
    
    NAME TYPE               ROOT_NAME ROOT_TYPE     ISLEAF
    ---- ------------------ --------- --------- ----------
    A    PROCEDURE          A         PROCEDURE          0
    B    PROCEDURE          A         PROCEDURE          1
    C    PROCEDURE          A         PROCEDURE          1
    B    PROCEDURE          B         PROCEDURE          0
    F    FUNCTION           B         PROCEDURE          1
    C    PROCEDURE          C         PROCEDURE          0
    F    FUNCTION           C         PROCEDURE          1
    D    PROCEDURE          D         PROCEDURE          1
    F    FUNCTION           F         FUNCTION           0
    

    then filter the leaf nodes and combine the callers:

    select root_name, root_type,
      listagg(case when name = root_name then null else name end, ', ')
        within group (order by name) as callers
    from (
      select name, type, connect_by_root(name) as root_name,
        connect_by_root(type) as root_type, connect_by_isleaf as isleaf
      from user_identifiers
      start with object_type = 'PACKAGE BODY'
      and object_name = 'P42'
      and type in ('FUNCTION', 'PROCEDURE')
      and usage = 'DEFINITION'
      connect by object_type = prior object_type
      and object_name = prior object_name
      and usage_context_id = prior usage_id
    )
    where type in ('FUNCTION', 'PROCEDURE')
    and isleaf = 1
    group by root_name, root_type;
    
    ROOT_NAME ROOT_TYPE CALLERS            
    --------- --------- --------------------
    A         PROCEDURE B, C                
    B         PROCEDURE F                   
    C         PROCEDURE F                   
    D         PROCEDURE                     
    

    But that doesn't show F; you can add an outer join to get that:

    with ui as (
      select * from user_identifiers
      where object_type = 'PACKAGE BODY'
      and object_name = 'P42'
    ),
    calls as (
      select object_type, object_name, name, type, signature,
        connect_by_root(name) as root_name,
        connect_by_root(type) as root_type,
        connect_by_root(signature) as root_signature,
        connect_by_isleaf as isleaf
      from ui
      start with type in ('FUNCTION', 'PROCEDURE')
      and usage = 'DEFINITION'
      connect by usage_context_id = prior usage_id
      and prior usage != 'CALL'
    )
    select ui.name, ui.type,
      listagg(case when c.name = c.root_name then null else c.name end, ', ')
        within group (order by c.name) as callers
    from ui
    left join calls c on c.object_type = ui.object_type
    and c.object_name = ui.object_name
    and c.root_type = ui.type
    and c.root_name = ui.name
    and c.root_signature = ui.signature
    and c.type in ('FUNCTION', 'PROCEDURE')
    and c.isleaf = 1
    where ui.type in ('FUNCTION', 'PROCEDURE')
    and ui.usage = 'DEFINITION'
    group by ui.name, ui.type;
    
    NAME TYPE               CALLERS            
    ---- ------------------ --------------------
    A    PROCEDURE          B, C                
    B    PROCEDURE          F                   
    C    PROCEDURE          F                   
    D    PROCEDURE                              
    F    FUNCTION                               
    

    I'm pretty sure that can be simplified...

    Getting the main/slave/independent flag is a bit trickier; you need to decide what each of those means (e.g. main has calls going out but no calls coming in; independent has no calls in or out; slave everything else) possibly with further joins to figure things out.

    So this is a starting point that gets you some of the information and hopefully points to things to explore to get all the information you need, in the format you need it.

    0 讨论(0)
提交回复
热议问题