Oracle 构建顺序和 PL/SQL 包依赖关系
Posted
技术标签:
【中文标题】Oracle 构建顺序和 PL/SQL 包依赖关系【英文标题】:Oracle build order and PL/SQL package dependencies 【发布时间】:2010-02-02 20:39:24 【问题描述】:我正在尝试建立一个 PL/SQL 包依赖项列表,以便我可以帮助设置一个自动构建脚本,以便我的包在测试服务器上运行。有没有办法从单个包(理想情况下由名称标识的“根”包)开始,然后找到所有依赖项以及它们必须编译的顺序? 依赖关系已经在我的个人架构中完全解决了(所以至少我有一个地方可以开始——但我下一步该去哪里呢?)。
(甲骨文 10.2)
编辑:
正在使用的构建工具将使用构建顺序,并从源代码控制中按顺序检索这些文件,然后将它们传递给 Oracle 进行编译(实际的构建工具本身是用 Python 或 Java 编写的 - 我无权访问源代码)。基本上,构建工具需要一个文件列表作为输入,按照它们必须在其中编译的顺序进行编译,并在源代码控制中访问这些文件。如果有,一切都会很好。
编辑:
感谢您提供简洁的脚本。不幸的是,构建过程大多不在我的掌控之中。该过程基于一个构建工具,该工具由我们正在集成的产品的供应商构建,这就是为什么我可以为构建过程提供的唯一输入是按需要构建的顺序排列的文件列表。如果出现编译器错误,构建工具失败,我们必须手动提交新构建的请求。因此,按照编译顺序列出的文件很重要。
编辑:
找到这个:http://www.oracle.com/technology/oramag/code/tips2004/091304.html 给我任何对象的依赖关系。现在我只需要正确排序......如果我得到一些有用的东西,我会在这里发布。
编辑:(带代码!)
我知道,一般来说,这种事情对于 Oracle 来说是不必要的,但对于任何仍然感兴趣的人来说......
我拼凑了一个小脚本,它似乎能够获得构建顺序,这样所有包都将以正确的顺序构建,并且第一次不会出现与依赖相关的错误(关于 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_dependencies
和all_dependencies
,现在这段代码可能会变得更简单。
【问题讨论】:
你的意思是Oracle PL/SQL 包吗? 我完成它的唯一方法是编写脚本,并测试部署到新模式。根据错误根据需要对脚本中的包重新排序... 我忘了提到应该在 bdy 之前加载 spc 以帮助解析 package.function/stored proc。您将它们分开,而不是加载单个文件(文件扩展名让我 ATM 转义) - 对吗? @OMG Ponies:是的,我们知道我们必须在任何机构之前编译所有规范。主要问题是规范的顺序。是的,它们位于与正文不同的文件中。 【参考方案1】:如果您真的只处理 PL/SQL 包,则无需担心构建顺序。只需先构建所有包规范。然后你可以部署所有的包体,它们会编译,因为它们的依赖是包规范。
如果你碰巧有一些依赖于其他规范的包规范——如果你有声明了常量、子类型或引用游标的包,这些包在打包过程的签名中使用——那么你需要构建这些包规格第一。但是它们应该足够少,您可以手动将它们排列在构建脚本中。
编辑
看起来他们会做 增量和“彻底”构建, 所以构建顺序最重要 因为当他们清理 环境并重建它。
这不会改变任何事情。
这是一个扩展示例。我有一个包含三个包的架构....
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>
所有依赖的对象都是包体,所有引用的对象都是打包的规范。因此,如果我丢弃'n'重建模式,我使用什么顺序真的无关紧要。首先我们垃圾......
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
构建的任何东西都会被创建,如果有错误,它只会被标记为无效。一旦我们直接或间接地执行它,数据库就会为我们编译它。所以,顺序并不重要。真的没有。
如果您担心使用无效对象完成构建的想法 - 我对此表示同情,我们被告知不要忍受破碎的窗户 - 您可以使用 utlrp
脚本或 11g 中的 the UTL_RECOMP package;任何一种方法都需要 SYSDBA 帐户。
编辑 2
该过程基于构建 由供应商构建的工具 我们正在集成的产品, 这就是为什么我唯一可以输入的原因 给构建过程的是一个列表 按需要的顺序排列文件 内置。如果有编译器 错误,构建工具失败,我们有 手动提交新的请求 构建。
这是一个政治问题,而不是技术问题。这并不是说政治问题不能通过技术修复来解决,只是技术修复不是完成这项工作的最佳工具。祝你好运。
【讨论】:
我开始手工操作,并意识到随着项目的进展和新包的添加,我们将不得不继续手工操作(并且将会有新包,可能是在春末 - 我希望在那之前有一些自动化的东西)。 无论如何都要自动化构建。但是如果你现在添加一个包,你只需要将它添加到构建脚本的末尾。依赖顺序与包无关。 如果他们以增量方式构建代码,我认为您是对的。看起来他们将进行增量和“清扫”构建,因此构建顺序对于他们清理环境并重建环境最重要。 把所有的包都推进去之后,如果你看一下schema,很多可能会出现未编译的情况。如前所述,Oracle 应在首次使用时自动编译它们,但如果您想要更舒适,只需使用 dbms_utility.compile_schema(...) 来编译您的模式。它可能类似于 UTLRP,但不需要 SYSDBA 权限并且只影响一个模式。 这些都是非常好的观点。我们在工具本身方面也取得了某种胜利:我们刚刚发现我们可以“在幕后”请求多达 4 次 PL/SQL 重新编译,并且只报告第 4 次通过后仍然存在的错误。我仍然想要一个基本正确的构建顺序,因为它可以帮助 4 次传递更快一点。【参考方案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;
/
【讨论】:
有趣,但我不确定这会提供实际的构建顺序。【参考方案3】:遍历依赖关系树时需要注意的一点。未编译程序的依赖项不显示...
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
的主体仍然无效,因此以下查询不会返回其对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>
【讨论】:
【参考方案4】:您不需要构建顺序 - 只需在逐个文件的基础上使用“CREATE OR REPLACE...”构建包,然后在两级嵌套循环中编译它们 - 每次都在内部传递循环编译仍然无效的所有内容,外部循环用于检查剩余无效对象的计数,并为内部循环的最大执行设置某种阈值。在实践中,我从未见过需要超过 3 次的传球次数。
如果您在依赖项中涉及多个模式,请查看运行 Oracle 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;
/
【讨论】:
【参考方案5】:将以下命令添加到脚本顶部:
设置验证关闭
这将使您的脚本无需验证即可运行,因此可以按任何顺序运行。
您可以稍后查询 DBA_ERRORS 以获取包、视图和类型中的所有错误和警告。
【讨论】:
SQL Plus 命令SET VERIFY OFF
仅 轮流验证替换变量值,例如&schema_name.
【参考方案6】:
在 11.1 及更高版本中尝试此方法。以任意顺序运行脚本。 最后发出以下命令: (更改命令参数以满足您的需要)
-- Compile invalid objects
EXEC DBMS_UTILITY.compile_schema(USER, FALSE);
更多详情请关注DBMS_UTILITY.compile_scema
【讨论】:
【参考方案7】:实际解决方案:上面的脚本似乎给出了正确的构建顺序。可能可以重写为“更好”,但我将把它作为练习留给读者。 ;)
经过一番讨论,构建工具将在报告错误之前连续执行 n(实际上是 4 次)构建。如果构建顺序错误,这也将有助于解决依赖编译错误,但我宁愿在第一次获得正确的构建顺序。
【讨论】:
以上是关于Oracle 构建顺序和 PL/SQL 包依赖关系的主要内容,如果未能解决你的问题,请参考以下文章
Oracle PL/SQL 查询顺序按 Distinct 问题
PL/SQl和SQL servers是啥关系?他们直接有联系吗?