首页 文章

Oracle构建顺序和PL / SQL包依赖项

提问于
浏览
7

我正在尝试 Build 一个PL / SQL包依赖项列表,以便我可以帮助为我的包设置一个自动构建脚本,以便在测试服务器上运行 . 有没有办法从单个包开始(理想情况下,由名称标识的“根”包),然后查找所有依赖项,以及它们必须编译的顺序?依赖关系已在我的个人架构中完全解决(所以至少我有一个地方可以开始 - 但我下一步该去哪里?) .

(Oracle 10.2)

EDIT:

正在使用的构建工具将使用构建顺序,并将从源代码控制中按顺序检索这些文件,然后将它们传递给Oracle进行编译(实际的构建工具本身是用Python或Java编写的,或者两者兼而有之 - 我不是有权访问来源) . 基本上,构建工具需要输入一个文件列表,按照它们必须编译的顺序进行编译,并在源代码管理中访问这些文件 . 如果它有,那么一切都会很好 .

EDIT:

感谢整洁的脚本 . 不幸的是,构建过程主要不在我手中 . 该过程基于构建工具,该工具由我们正在集成的产品的供应商构建,这就是为什么我可以为构建过程提供的唯一输入是按照需要构建的顺序的文件列表 . 如果存在编译器错误,则构建工具失败,我们必须手动提交新构建的请求 . 因此,按照编译顺序排列的文件列表非常重要 .

EDIT:

发现这个:http://www.oracle.com/technology/oramag/code/tips2004/091304.html给我任何对象的依赖 . 现在我只需要正确的订购...如果我得到了一些工作,我会在这里发布 .

EDIT: (带代码!)

我知道,总的来说,甲骨文不需要这种东西,但对于那些仍然感兴趣的人来说......

我拼凑了一个似乎能够获得构建顺序的小脚本,这样所有的包都将以正确的顺序构建,没有依赖相关的错误(关于pacakges)第一次:

declare

    type t_dep_list is table of varchar2(40) index by binary_integer;
    dep_list t_dep_list;
    i number := 1;
    cursor c_getObjDepsByNameAndType is
    --based on a query found here: http://www.oracle.com/technology/oramag/code/tips2004/091304.html
        select lvl, u.object_id, u.object_type, LPAD(' ', lvl) || object_name obj
        FROM (SELECT level lvl, object_id
               FROM SYS.public_dependency s
               START WITH s.object_id = (select object_id
                                         from user_objects
                                         where object_name = UPPER(:OBJECT_NAME)
                                               and object_type = UPPER(:OBJECT_TYPE))
               CONNECT BY s.object_id = PRIOR referenced_object_id
               GROUP BY level, object_id) tree, user_objects u
        WHERE tree.object_id = u.object_id
              and u.object_type like 'PACKAGE%' --only look at packages, not interested in other types of objects
        ORDER BY lvl desc;

    function fn_checkInList(in_name in varchar2) return boolean is
    begin
        for j in 1 .. dep_list.count loop
            if dep_list(j) = in_name then
                return true;
            end if;
        end loop;
        return false;
    end;



    procedure sp_getDeps(in_objID in user_objects.object_id%type, in_name in varchar2) is
        cursor c_getObjDepsByID(in_objID in user_objects.object_id%type) is
        --based on a query found here: http://www.oracle.com/technology/oramag/code/tips2004/091304.html
            select lvl, u.object_id, u.object_type, LPAD(' ', lvl) || object_name obj
            FROM (SELECT level lvl, object_id
                   FROM SYS.public_dependency s
                   START WITH s.object_id = (select uo.object_id
                                             from user_objects uo
                                             where uo.object_name =
                                                   (select object_name from user_objects uo where uo.object_id = in_objID)
                                                   and uo.object_type = 'PACKAGE BODY')
                   CONNECT BY s.object_id = PRIOR referenced_object_id
                   GROUP BY level, object_id) tree, user_objects u
            WHERE tree.object_id = u.object_id
                  and u.object_id <> in_objID --exclude self (requested Object ID) from list.
            ORDER BY lvl desc;
    begin
        --loop through the dependencies
        for r in c_getObjDepsByID(in_objID) loop
            if fn_checkInList(trim(r.obj)) = false and (r.object_type = 'PACKAGE' or r.object_type = 'PACKAGE BODY') and
               trim(r.obj) <> trim(in_name) then
                dbms_output.put_line('checking deps of: ' || r.obj || ' ' || r.object_id || ' level: ' || r.lvl);
                --now for each dependency, check the sub-dependency
                sp_getDeps(r.object_id, trim(r.obj));
                --add the object to the dependency list.
                dep_list(i) := trim(r.obj);
                i := i + 1;
            end if;
        end loop;
    exception
        when NO_DATA_FOUND then
            dbms_output.put_line('no more data for: ' || in_objID);
    end;

begin

    for r in c_getObjDepsByNameAndType loop
        dbms_output.put_line('top-level checking deps of: ' || r.obj || ' ' || r.object_id || ' level: ' || r.lvl);
        sp_getDeps(r.object_id, trim(r.obj));
    end loop;

    dbms_output.put_line('dep count: ' || dep_list.count);
    for j in 1 .. dep_list.count loop
        dbms_output.put_line('obj: ' || j || ' ' || dep_list(j));
    end loop;
end;

我知道这不是最漂亮的代码(遍布整个地方的全局,等等......)如果我今天下午有机会清理它,我可能会重新发布它,但是现在,它产生了一个构建顺序这似乎第一次运行没有问题 .

:OBJECT_NAME 应该是您要跟踪所有依赖项和构建顺序的根对象 . 对我来说,这是一个包含单个方法的主程序包,它是系统其余部分的入口点 .

:OBJECT_TYPE 我主要限制在 PACKAGE BODY ,但包含其他类型(如触发器)不应该太多 .

最后一点, :OBJECT_NAME 指定的对象不会出现在输出中,但它应该是最后一项,因此您必须手动将其添加到构建列表中 .

UPDATE: 我刚刚发现 user_dependenciesall_dependencies ,这段代码现在可能变得更加简单了 .

7 回答

  • 2

    将以下命令添加到脚本的顶部:

    设置验证关闭

    这将允许您的脚本无需验证即可运行,因此可以按任何顺序运行 .

    您可以稍后查询DBA_ERRORS以获取包,视图和类型中的所有错误和警告 .

  • 0

    请尝试使用11.1及更高版本 . 以任何顺序运行脚本 . 最后发出以下命令:(更改命令参数以满足您的需要)

    -- Compile invalid objects
    EXEC DBMS_UTILITY.compile_schema(USER, FALSE);
    

    关于DBMS_UTILITY.compile_scema的更多细节

  • 0

    实际解决方案:上面的脚本似乎给出了正确的构建顺序 . 可能会被重写“更好”,但我会将其作为练习留给读者 . ;)

    经过一番讨论后,构建工具将在报告错误之前连续执行n(4个实际)构建 . 如果构建顺序错误,这也有助于解决依赖编译错误,但我宁愿第一次获得构建顺序 .

  • 8

    如果您真的只处理PL / SQL包,那么您不需要为构建顺序而烦恼 . 首先构建所有包规范 . 然后你可以部署所有的包体,它们将进行编译,因为它们的依赖关系是包规范 .

    如果您碰巧有一些依赖于其他规范的软件包规范 - 如果您有声明的包,例如,在打包过程的签名中使用的常量,子类型或ref游标 - 那么您需要首先构建这些软件包规范 . 但是应该没有足够的它们可以手动在构建脚本中排列它们 .

    edit

    看起来他们将进行增量和“清理扫描”构建,因此构建顺序对于清理环境并重建它时最重要 .

    这不会改变任何事情 .

    这是一个扩展的例子 . 我有一个包含三个包的模式....

    SQL> select object_name, object_type, status
      2  from user_objects
      3  order by 1, 2
      4  /
    
    OBJECT_NAME     OBJECT_TYPE     STATUS
    --------------- --------------- -------
    PKG1            PACKAGE         VALID
    PKG1            PACKAGE BODY    VALID
    PKG2            PACKAGE         VALID
    PKG2            PACKAGE BODY    VALID
    PKG3            PACKAGE         VALID
    PKG3            PACKAGE BODY    VALID
    
    6 rows selected.
    
    SQL>
    

    有趣的是PKG1中的过程从PKG2调用过程,PKG2中的过程从PKG3调用过程,PKG3中的过程从PKG1调用过程 .

    Q. 循环依赖如何运作?
    A. 这不是循环依赖......

    SQL> select name, type, referenced_name, referenced_type
      2  from user_dependencies
      3  where referenced_owner = user
      4  /
    
    NAME            TYPE            REFERENCED_NAME REFERENCED_TYPE
    --------------- --------------- --------------- ---------------
    PKG1            PACKAGE BODY    PKG1            PACKAGE
    PKG1            PACKAGE BODY    PKG2            PACKAGE
    PKG2            PACKAGE BODY    PKG2            PACKAGE
    PKG2            PACKAGE BODY    PKG3            PACKAGE
    PKG3            PACKAGE BODY    PKG3            PACKAGE
    PKG3            PACKAGE BODY    PKG1            PACKAGE
    
    6 rows selected.
    
    SQL>
    

    所有依赖对象都是包体,所有引用的对象都是打包的规范 . 因此,如果我无法构建架构,那么我使用的顺序并不重要 . 首先我们垃圾......

    SQL> drop package pkg1
      2  /
    
    Package dropped.
    
    SQL> drop package pkg2
      2  /
    
    Package dropped.
    
    SQL> drop package pkg3
      2  /
    
    Package dropped.
    
    SQL>
    

    然后我们重建......

    SQL> create or replace package pkg3 is
      2      procedure p5;
      3      procedure p6;
      4  end pkg3;
      5  /
    
    Package created.
    
    SQL> create or replace package pkg2 is
      2      procedure p3;
      3      procedure p4;
      4  end pkg2;
      5  /
    
    Package created.
    
    SQL> create or replace package pkg1 is
      2      procedure p1;
      3      procedure p2;
      4  end pkg1;
      5  /
    
    Package created.
    
    SQL> create or replace package body pkg2 is
      2      procedure p3 is
      3      begin
      4          pkg3.p5;
      5      end p3;
      6      procedure p4 is
      7      begin
      8          dbms_output.put_line('PKG2.P4');
      9      end p4;
     10  end pkg2;
     11  /
    
    Package body created.
    
    SQL> create or replace package body pkg3 is
      2      procedure p5 is
      3      begin
      4          dbms_output.put_line('PKG3.P5');
      5      end p5;
      6      procedure p6 is
      7      begin
      8          pkg1.p1;
      9      end p6;
     10  end pkg3;
     11  /
    
    Package body created.
    
    SQL> create or replace package body pkg1 is
      2      procedure p1 is
      3      begin
      4          dbms_output.put_line('PKG1.P1');
      5      end p1;
      6      procedure p2 is
      7      begin
      8          pkg2.p4;
      9      end p2;
     10  end pkg1;
     11  /
    
    Package body created.
    
    SQL>
    

    各个对象的顺序无关紧要 . 只需在包体之前构建包规范 . 虽然即使这并不重要......

    SQL> create or replace package pkg4 is
      2      procedure p7;
      3  end pkg4;
      4  /
    
    Package created.
    
    SQL> create or replace package body pkg4 is
      2      procedure p7 is
      3      begin
      4          dbms_output.put_line('PKG4.P7::'||constants_pkg.whatever);
      5      end p7;
      6  end pkg4;
      7  /
    
    Warning: Package Body created with compilation errors.
    
    SQL> show errors
    Errors for PACKAGE BODY PKG4:
    
    LINE/COL ERROR
    -------- -----------------------------------------------------------------
    4/9      PL/SQL: Statement ignored
    4/43     PLS-00201: identifier 'CONSTANTS_PKG.WHATEVER' must be declared
    SQL>
    

    PKG4 是无效的,因为我们尚未构建 CONSTANTS_PKG .

    SQL> create or replace package constants_pkg is
      2      whatever constant varchar2(20) := 'WHATEVER';
      3  end constants_pkg;
      4  /
    
    Package created.
    
    SQL> select object_name, object_type, status
      2  from user_objects
      3  where status != 'VALID'
      4  order by 1, 2
      5  /
    
    OBJECT_NAME     OBJECT_TYPE     STATUS
    --------------- --------------- -------
    PKG4            PACKAGE BODY    INVALID
    
    SQL> 
    SQL> set serveroutput on size unlimited
    SQL> exec pkg4.p7
    PKG4.P7::WHATEVER
    
    PL/SQL procedure successfully completed.
    
    SQL> select object_name, object_type, status
      2  from user_objects
      3  where status != 'VALID'
      4  order by 1, 2
      5  /
    
    no rows selected
    
    SQL>
    

    始终使用 CREATE OR REPLACE 构建任何内容,如果存在错误,则仅将其标记为INVALID . 一旦我们直接或间接地执行它,数据库就会为我们编译它 . 所以,顺序无所谓 . 真的没有 .

    如果使用无效对象完成构建的想法与您有关 - 我对此有一些同情,我们被告知不要使用破窗 - 您可以使用 utlrp 脚本或11g the UTL_RECOMP package;任何一种方法都需要一个SYSDBA帐户 .

    edit 2

    该过程基于构建工具,该工具由我们正在集成的产品的供应商构建,这就是为什么我可以为构建过程提供的唯一输入是按照需要构建的顺序的文件列表如果存在编译器错误,构建工具失败,我们必须手动提交新构建的请求 .

    这是一个政治问题而不是技术问题 . 这并不是说政治问题无法通过技术修复来解决,只是说技术修复不是工作的最佳工具 . 祝好运 .

  • 1

    http://www.oracle-base.com/articles/misc/RecompilingInvalidSchemaObjects.php查看以下脚本

    SET SERVEROUTPUT ON SIZE 1000000
    BEGIN
      FOR cur_rec IN (SELECT owner,
                             object_name,
                             object_type,
                             DECODE(object_type, 'PACKAGE', 1,
                                                 'PACKAGE BODY', 2, 2) AS recompile_order
                      FROM   dba_objects
                      WHERE  object_type IN ('PACKAGE', 'PACKAGE BODY')
                      AND    status != 'VALID'
                      ORDER BY 4)
      LOOP
        BEGIN
          IF cur_rec.object_type = 'PACKAGE' THEN
            EXECUTE IMMEDIATE 'ALTER ' || cur_rec.object_type || 
                ' "' || cur_rec.owner || '"."' || cur_rec.object_name || '" COMPILE';
          ElSE
            EXECUTE IMMEDIATE 'ALTER PACKAGE "' || cur_rec.owner || 
                '"."' || cur_rec.object_name || '" COMPILE BODY';
          END IF;
        EXCEPTION
          WHEN OTHERS THEN
            DBMS_OUTPUT.put_line(cur_rec.object_type || ' : ' || cur_rec.owner || 
                                 ' : ' || cur_rec.object_name);
        END;
      END LOOP;
    END;
    /
    
  • 0

    走依赖树时要注意的一件小事 . 未编译程序的依赖关系未显示...

    SQL> drop package constants_pkg
      2  /
    
    Package dropped.
    
    SQL> create or replace package body pkg4 is
      2      procedure p7 is
      3      begin
      4          dbms_output.put_line('PKG4.P7::'||zzz_constants_pkg.whatever);
      5      end p7;
      6  end pkg4;
      7  /
    
    Warning: Package Body created with compilation errors.
    
    SQL> show errors
    Errors for PACKAGE BODY PKG4:
    
    LINE/COL ERROR
    -------- -----------------------------------------------------------------
    4/9      PL/SQL: Statement ignored
    4/43     PLS-00201: identifier 'ZZZ_CONSTANTS_PKG.WHATEVER' must be
             declared
    
    SQL>
    

    因此, PKG4 的主体是无效的,因为 ZZZ_CONSTANTS_PKG 不存在 .

    SQL> create or replace package zzz_constants_pkg is
      2      whatever constant varchar2(20) := 'WHATEVER';
      3  end zzz_constants_pkg;
      4  /
    
    Package created.
    
    SQL>
    

    PKG4 的主体仍然是INVALID,因此以下查询不会返回其对 ZZZ_CONSTANTS_PKG 的依赖....

    SQL> select name, type, referenced_name, referenced_type
      2  from user_dependencies
      3  where referenced_owner = user
      4  /
    
    NAME            TYPE            REFERENCED_NAME   REFERENCED_TYPE
    --------------- --------------- ----------------- ---------------
    PKG1            PACKAGE BODY    PKG1              PACKAGE
    PKG1            PACKAGE BODY    PKG2              PACKAGE
    PKG2            PACKAGE BODY    PKG2              PACKAGE
    PKG2            PACKAGE BODY    PKG3              PACKAGE
    PKG3            PACKAGE BODY    PKG3              PACKAGE
    PKG3            PACKAGE BODY    PKG1              PACKAGE
    PKG4            PACKAGE BODY    PKG4              PACKAGE
    
    7 rows selected.
    
    SQL>
    

    现在让我们编译 PKG4 并重新查询依赖项....

    SQL> alter package pkg4 compile body;
    
    Package body altered.
    
    SQL> select name, type, referenced_name, referenced_type
      2  from user_dependencies
      3  where referenced_owner = user
      4  /
    
    NAME            TYPE            REFERENCED_NAME   REFERENCED_TYPE
    --------------- --------------- ----------------- ---------------
    PKG1            PACKAGE BODY    PKG1              PACKAGE
    PKG1            PACKAGE BODY    PKG2              PACKAGE
    PKG2            PACKAGE BODY    PKG2              PACKAGE
    PKG2            PACKAGE BODY    PKG3              PACKAGE
    PKG3            PACKAGE BODY    PKG3              PACKAGE
    PKG3            PACKAGE BODY    PKG1              PACKAGE
    PKG4            PACKAGE BODY    PKG4              PACKAGE
    PKG4            PACKAGE BODY    ZZZ_CONSTANTS_PKG PACKAGE
    
    8 rows selected.
    
    SQL>
    
  • -1

    您不需要构建顺序 - 只需逐个文件地使用“CREATE OR REPLACE ...”构建包,然后在两级嵌套循环中编译它们 - 内部循环中的每个传递都会编译所有内容仍然无效,外部循环用于检查剩余无效对象的数量,并设置某种阈值以最大限度地执行内部循环 . 在实践中,我从未见过所需的传球次数高于三次 .

    如果您有多个模式涉及依赖项,请查看运行Oracles utlrp.sql脚本,该脚本适用于模式并设置一些基础结构来管理流程 - 但这需要一个特权帐户 .

    此外,如果将源代码控制扩展为包含视图,请确保脚本使用“CREATE OR REPLACE FORCE VIEW ...”来创建在创建时具有未满足依赖关系的视图 .

    我使用的示例脚本:

    set serveroutput on
    declare
     cursor invalidObjCur is 
      select object_name, object_type
        from user_objects
        where status <> 'VALID'
        ;
     compileStmt varchar2(4000); 
     passCount pls_integer := 0;
     maxPasses pls_integer := 5;
     lastInvalidCount pls_integer := 32000; 
     objectCount pls_integer;
     continue boolean := TRUE;
    
    begin
     dbms_output.enable(1000000);
     while (continue) loop
       passCount := passCount + 1;
       dbms_output.put_line('Pass '||passCount);
       objectCount := 0;
       for curRow in InvalidObjCur loop
        if curRow.object_type = 'PACKAGE BODY' then
            compileStmt := 'alter PACKAGE '||curRow.object_name||' compile body';
        else
            compileStmt := 'alter '||curRow.object_type||' '||
            chr(34)||curRow.object_name||chr(34)||' compile';
        end if;
        begin
          execute immediate compileStmt;
        exception when others then
          null;
        end;
       objectCount := objectCount + 1;
       end loop;
       dbms_output.put_line('Recompilations attempted: '||objectCount);
       continue := (passCount < maxPasses) and (objectCount < lastInvalidCount);
       lastInvalidCount := objectCount;
     end loop;
    dbms_output.put_line('***** Remaining Invalid ********');
    for curRow in InvalidObjCur loop
     dbms_output.put_line(curRow.object_type||' '||curRow.object_name);
    end loop; 
    dbms_output.put_line('********************************');
    end;    
    /
    

相关问题