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

时间:2010-02-02 20:39:24

标签: oracle plsql dependencies

我正在尝试建立一个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;

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

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

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

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

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

7 个答案:

答案 0 :(得分: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>

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

问。该循环依赖如何运作? 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 {{3 }};任何一种方法都需要一个SYSDBA帐户。

编辑2

  

该过程基于构建   由供应商建造的工具   我们正在整合的产品,   这就是为什么我能做的唯一输入   给构建过程一个列表   文件按他们需要的顺序排列   内置。如果有编译器   错误,构建工具失败,我们有   手动提交新请求   建立。

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

答案 1 :(得分:2)

查看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;
/

答案 2 :(得分:1)

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

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> 

答案 3 :(得分:0)

您不需要构建顺序 - 只需逐个文件地使用“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;    
/

答案 4 :(得分:0)

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

SET VERIFY OFF

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

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

答案 5 :(得分:0)

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

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

有关DBMS_UTILITY.compile_scema

的更多详情

答案 6 :(得分:-1)

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

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