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

Fru*_*ner 7 oracle dependencies plsql

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

(Oracle 10.2)

编辑:

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

编辑:

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

编辑:

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

编辑:(带代码!)

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

我拼凑了一个似乎能够获得构建顺序的小脚本,这样所有的包都将以正确的顺序构建,没有依赖相关的错误(关于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;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

更新:我刚刚发现user_dependencies并且all_dependencies,这个代码也许可以现在简单得多.

APC*_*APC 8

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

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

编辑

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

这不会改变任何事情.

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

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>
Run Code Online (Sandbox Code Playgroud)

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

:循环依赖如何工作?
答: 这不是循环依赖......

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> 
Run Code Online (Sandbox Code Playgroud)

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

SQL> drop package pkg1
  2  /

Package dropped.

SQL> drop package pkg2
  2  /

Package dropped.

SQL> drop package pkg3
  2  /

Package dropped.

SQL>
Run Code Online (Sandbox Code Playgroud)

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

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>
Run Code Online (Sandbox Code Playgroud)

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

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

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

编辑2

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

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


Fru*_*ner -1

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

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