Порядок сборки 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, фактически) строит в строке перед сообщением об ошибках. Это также поможет устранить ошибки компиляции зависимостей, если порядок сборки неправильный, но я бы предпочел просто получить порядок сборки в первый раз.