我正在尝试 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_dependencies
和 all_dependencies
,这段代码现在可能变得更加简单了 .
7 回答
将以下命令添加到脚本的顶部:
设置验证关闭
这将允许您的脚本无需验证即可运行,因此可以按任何顺序运行 .
您可以稍后查询DBA_ERRORS以获取包,视图和类型中的所有错误和警告 .
请尝试使用11.1及更高版本 . 以任何顺序运行脚本 . 最后发出以下命令:(更改命令参数以满足您的需要)
关于DBMS_UTILITY.compile_scema的更多细节
实际解决方案:上面的脚本似乎给出了正确的构建顺序 . 可能会被重写“更好”,但我会将其作为练习留给读者 . ;)
经过一番讨论后,构建工具将在报告错误之前连续执行n(4个实际)构建 . 如果构建顺序错误,这也有助于解决依赖编译错误,但我宁愿第一次获得构建顺序 .
如果您真的只处理PL / SQL包,那么您不需要为构建顺序而烦恼 . 首先构建所有包规范 . 然后你可以部署所有的包体,它们将进行编译,因为它们的依赖关系是包规范 .
如果您碰巧有一些依赖于其他规范的软件包规范 - 如果您有声明的包,例如,在打包过程的签名中使用的常量,子类型或ref游标 - 那么您需要首先构建这些软件包规范 . 但是应该没有足够的它们可以手动在构建脚本中排列它们 .
edit
这不会改变任何事情 .
这是一个扩展的例子 . 我有一个包含三个包的模式....
有趣的是PKG1中的过程从PKG2调用过程,PKG2中的过程从PKG3调用过程,PKG3中的过程从PKG1调用过程 .
Q. 循环依赖如何运作?
A. 这不是循环依赖......
所有依赖对象都是包体,所有引用的对象都是打包的规范 . 因此,如果我无法构建架构,那么我使用的顺序并不重要 . 首先我们垃圾......
然后我们重建......
各个对象的顺序无关紧要 . 只需在包体之前构建包规范 . 虽然即使这并不重要......
PKG4
是无效的,因为我们尚未构建CONSTANTS_PKG
.始终使用
CREATE OR REPLACE
构建任何内容,如果存在错误,则仅将其标记为INVALID . 一旦我们直接或间接地执行它,数据库就会为我们编译它 . 所以,顺序无所谓 . 真的没有 .如果使用无效对象完成构建的想法与您有关 - 我对此有一些同情,我们被告知不要使用破窗 - 您可以使用
utlrp
脚本或11g the UTL_RECOMP package;任何一种方法都需要一个SYSDBA帐户 .edit 2
这是一个政治问题而不是技术问题 . 这并不是说政治问题无法通过技术修复来解决,只是说技术修复不是工作的最佳工具 . 祝好运 .
从http://www.oracle-base.com/articles/misc/RecompilingInvalidSchemaObjects.php查看以下脚本
走依赖树时要注意的一件小事 . 未编译程序的依赖关系未显示...
因此,
PKG4
的主体是无效的,因为ZZZ_CONSTANTS_PKG
不存在 .但
PKG4
的主体仍然是INVALID,因此以下查询不会返回其对ZZZ_CONSTANTS_PKG
的依赖....现在让我们编译
PKG4
并重新查询依赖项....您不需要构建顺序 - 只需逐个文件地使用“CREATE OR REPLACE ...”构建包,然后在两级嵌套循环中编译它们 - 内部循环中的每个传递都会编译所有内容仍然无效,外部循环用于检查剩余无效对象的数量,并设置某种阈值以最大限度地执行内部循环 . 在实践中,我从未见过所需的传球次数高于三次 .
如果您有多个模式涉及依赖项,请查看运行Oracles utlrp.sql脚本,该脚本适用于模式并设置一些基础结构来管理流程 - 但这需要一个特权帐户 .
此外,如果将源代码控制扩展为包含视图,请确保脚本使用“CREATE OR REPLACE FORCE VIEW ...”来创建在创建时具有未满足依赖关系的视图 .
我使用的示例脚本: