Порядок сборки Oracle и зависимости пакетов PL/SQL
Я пытаюсь создать список зависимостей пакетов PL/SQL, чтобы помочь настроить автоматический сценарий сборки для моих пакетов для запуска на тестовом сервере. Есть ли способ начать с одного пакета ("корневой" пакет, идентифицированный по имени, в идеале), а затем найти все зависимости и порядок их компиляции? Зависимости уже полностью разрешены в моей личной схеме (так что, по крайней мере, мне есть с чего начать - но куда мне идти дальше?).
(Oracle 10.2)
EDIT:
используемый инструмент сборки будет использовать порядок сборки и будет возвращать эти файлы в порядке из системы управления версиями, а затем передавать их Oracle для компиляции (сам инструмент сборки написан на Python или Java или обоих - у меня нет доступа к источнику). В принципе, инструмент сборки должен ввести список файлов для компиляции в том порядке, в котором они должны быть скомпилированы в, и доступ к этим файлам в системе управления версиями. Если так, то все будет хорошо.
EDIT:
Спасибо за аккуратные скрипты. К сожалению, процесс сборки в основном не в моих руках. Процесс основан на инструменте сборки, который был построен поставщиком продукта, с которым мы интегрируемся, поэтому единственными входными данными, которые я могу дать процессу сборки, являются список файлов в том порядке, в котором они должны быть встроены. Если есть ошибка компилятора, инструмент сборки терпит неудачу, мы должны вручную отправьте запрос на новую сборку. Поэтому важен список файлов в том порядке, в котором они должны быть скомпилированы.
EDIT:
нашел это:http://www.oracle.com/technology/oramag/code/tips2004/091304.html Дает мне зависимости любого объекта. Теперь мне просто нужно сделать правильный заказ... Если что-то заработает, я отправлю это сюда.
EDIT: (С кодом!)
Я знаю, что в целом этот что-то вроде этого не нужно для 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;
Я знаю, что это не самый красивый код (глобалы повсюду и т. д... ugh), и я, вероятно, перепечатаю его, если у меня будет возможность сегодня днем очистите его, но прямо сейчас он создает порядок сборки, который, кажется, запускается в первый раз без проблем.
:OBJECT_NAME
должен быть корневым объектом, который вы хотите отслеживать все зависимости и порядок построения. Для меня это основной пакет с одним методом, который является точкой входа в остальную часть системы.
:OBJECT_TYPE
Я в основном ограничился PACKAGE BODY
, но не должно быть слишком много работы для включения других типов, таких как триггеры.
последнее, объект, указанный :OBJECT_NAME
не будет отображаться в выходных данных, но это должен быть последний элемент, поэтому вам придется добавить его в список сборки вручную.
обновление: Я только что обнаружил user_dependencies
и all_dependencies
, этот код, вероятно, можно было бы сделать намного проще.
7 ответов
если вы действительно имеете дело только с пакетами PL/SQL, вам не нужно потеть порядок сборки. Просто сначала создайте все спецификации пакета. Затем вы можете развернуть все тела пакетов, и они будут компилироваться, потому что их зависимости являются спецификациями пакета.
если у вас есть некоторые спецификации пакета, которые зависят от других спецификаций - если у вас есть пакеты, которые объявляют, скажем, константы, подтипы или курсоры ref, которые используются в подписях упакованных процедур - затем вам нужно сначала создать эти спецификации пакета. Но их должно быть достаточно мало, чтобы вы могли расположить их в скрипте сборки вручную.
редактировать
похоже, что они будут делать инкрементные и" чистые " сборки, поэтому порядок сборки будет иметь наибольшее значение когда они очистят окружающая среда и перестроить его.
это ничего не меняет.
вот расширенный пример. Я есть схема с тремя пакетами....
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.
вопрос: как работает эта циклическая зависимость?
А. это не циклическая зависимость....
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>
все зависимые объекты являются телами пакетов, все ссылки объекты-это упакованные спецификации. Следовательно, если я trash'n'Rebuild схему это действительно не имеет значения, какой порядок я использую. Сначала мы мусор ...
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 в UTL_RECOMP пакет; любой подход требует учетной записи SYSDBA.
edit 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;
/
одна маленькая вещь, чтобы следить за при ходьбе дереве зависимостей. Зависимости для несжатых программ не отображаются...
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>
вам не нужен порядок сборки-просто создайте пакеты с помощью "создать или заменить"..."на основе файла за файлом, а затем скомпилировать их в двухуровневом вложенном цикле-каждый проход во внутреннем цикле компилирует все, что еще недействительно, и внешний цикл используется для проверки количества оставшихся недействительных объектов и установить какой-то порог для максимального выполнения внутреннего цикла. На практике я никогда не видел, чтобы количество проходов было больше трех.
Если у вас есть несколько схем, участвующих в зависимостях, посмотрите на запущенные оракулы utlrp.сценарий sql, который работает по схемам и настраивает некоторую инфраструктуру для управления процессом - однако для этого нужна привилегированная учетная запись.
кроме того, если вы расширяете исходный элемент управления для включения представлений, убедитесь, что сценарии используют "создать или заменить представление силы... "для создания представлений, которые имеют неудовлетворенные зависимости на момент их создания.
пример скрипта, который я использую:
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;
/
добавьте следующую команду в верхнюю часть скрипта:
SET VERIFY OFF
Это позволит вашим скриптам работать без проверки и, следовательно, может выполняться в любом порядке.
вы можете позже запросить DBA_ERRORS, чтобы получить все ошибки и предупреждения в пакетах, представлениях и типах.
попробуйте это вместо 11.1 и выше. Запустите сценарий в любом порядке. В конце выполните следующую команду: (Измените параметры команды в соответствии с вашими потребностями)
-- Compile invalid objects
EXEC DBMS_UTILITY.compile_schema(USER, FALSE);
подробнее о DBMS_UTILITY.compile_scema
фактическое решение: сценарий выше, кажется, дает правильный порядок сборки. Вероятно, можно было бы переписать "лучше", но я оставлю это как упражнение для читателя. ;)
посовещавшись, инструмент будет выполнять n (4, фактически) строит в строке перед сообщением об ошибках. Это также поможет устранить ошибки компиляции зависимостей, если порядок сборки неправильный, но я бы предпочел просто получить порядок сборки в первый раз.