Порядок сборки Oracle и зависимости пакетов PL / SQL - PullRequest
7 голосов
/ 02 февраля 2010

Я пытаюсь составить список зависимостей пакетов PL / SQL, чтобы помочь настроить скрипт автоматической сборки для моих пакетов, запускаемых на тестовом сервере. Есть ли способ начать с одного пакета («корневого» пакета, в идеале идентифицируемого по имени), а затем найти все зависимости и порядок их компиляции? Зависимости уже полностью разрешены в моей личной схеме (так что, по крайней мере, у меня есть с чего начать, но куда мне идти дальше?).

(Oracle 10.2)

EDIT:

Используемый инструмент компоновки будет использовать порядок компоновки и будет извлекать эти файлы в порядке от управления исходным кодом, а затем передавать их в Oracle для компиляции (сам инструмент компоновки написан на Python или Java или на обоих языках - I нет доступа к источнику). По сути, инструменту сборки необходим входной список файлов для компиляции в том порядке, в котором они должны быть скомпилированы в , и доступ к этим файлам в системе контроля версий. Если это так, все будет работать очень хорошо.

EDIT:

Спасибо за аккуратные сценарии. К сожалению, процесс сборки в основном не в моих руках. Процесс основан на инструменте сборки, который был создан поставщиком продукта, с которым мы интегрируемся, поэтому единственные входные данные, которые я могу дать процессу сборки, - это список файлов в том порядке, в котором они должны быть встроены. Если есть ошибка компилятора, инструмент сборки не работает, мы должны вручную отправить запрос на новую сборку. Поэтому важен список файлов в порядке их компиляции.

EDIT:

Нашел это: 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, этот код, возможно, теперь можно сделать намного проще.

Ответы [ 7 ]

8 голосов
/ 03 февраля 2010

Если вы действительно имеете дело только с 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.

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> 

Все зависимые объекты являются телами пакетов, все ссылочные объекты являются упакованными спецификациями. Следовательно, если я создаю схему, это действительно не имеет значения, какой порядок я использую. Сначала мы мусор ...

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.

редактировать 2

Процесс основан на сборке инструмент, который был построен продавцом продукт, с которым мы интегрируемся, Именно поэтому единственные входы, которые я могу дать процессу сборки список файлов в том порядке, в котором они должны быть встроенный. Если есть компилятор ошибка, инструмент сборки не работает, у нас есть вручную подать запрос на новый строить.

Это политическая проблема, а не техническая. Это не означает, что политические проблемы не могут быть решены с помощью технического исправления, просто это техническое исправление не лучший инструмент для работы. Удачи.

2 голосов
/ 02 февраля 2010

Посмотрите на следующий скрипт из http://www.oracle -base.com / article / 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;
/
1 голос
/ 03 февраля 2010

Одна маленькая вещь, на которую нужно обращать внимание при ходьбе по дереву зависимостей. Зависимости для некомпилированных программ не отображаются ...

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> 
0 голосов
/ 20 мая 2015

Попробуйте вместо 11.1 и выше. Запустите скрипт в любом порядке. В конце введите следующую команду: (Измените параметры команды в соответствии с вашими потребностями)

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

Подробнее о DBMS_UTILITY.compile_scema

0 голосов
/ 05 февраля 2010

Фактическое решение: приведенный выше скрипт, кажется, дает правильный порядок сборки. Возможно, можно было бы переписать «лучше», но я оставлю это в качестве упражнения для читателя. ;)

После некоторого обсуждения инструмент сборки выполнит n (фактически 4) сборки перед тем, как сообщить об ошибках. Это также помогло бы устранить ошибки компиляции зависимостей, если порядок сборки неправильный, но я бы предпочел просто получить правильный порядок сборки с первого раза.

0 голосов
/ 03 февраля 2010

Добавьте следующую команду в начало вашего скрипта:

SET VERIFY OFF

это позволит вашим сценариям работать без проверки и, следовательно, может выполняться в любом порядке.

Позже вы можете запросить DBA_ERRORS, чтобы получить все ошибки и предупреждения в ваших пакетах, представлениях и типах.

0 голосов
/ 03 февраля 2010

Вам не нужен порядок сборки - просто соберите пакеты с помощью команды «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;    
/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...