2010-02-02 6 views
7

Je tente de créer une liste de dépendances de packages PL/SQL afin de pouvoir configurer un script de génération automatique pour que mes packages s'exécutent sur le serveur de test. Est-il possible de commencer avec un seul paquet (un paquet "root" identifié par son nom, idéalement), puis de trouver toutes les dépendances, et l'ordre dans lequel elles doivent être compilées? Les dépendances sont déjà entièrement résolues dans mon schéma personnel (donc au moins j'ai un endroit pour commencer - mais où dois-je aller ensuite?).Ordre de construction Oracle et dépendances du package PL/SQL

(Oracle 10,2)

EDIT:

L'outil de construction qui est utilisé utilisera l'ordre de construction et sera retreive ces fichiers dans l'ordre de contrôle de code source, et de les transmettre ensuite à Oracle compile (l'outil de construction proprement dit est écrit en Python ou Java ou les deux - je n'ai pas accès à la source). Fondamentalement, l'outil de construction a besoin en entrée d'une liste de fichiers à compiler dans l'ordre où ils doivent être compilés en, et accéder à ces fichiers dans le contrôle de source. Si c'est le cas, tout fonctionnera très bien.

EDIT:

Merci pour les scripts soignées. Malheureusement, le processus de construction est la plupart du temps hors de mes mains. Le processus est basé sur un outil de construction qui a été construit par le fournisseur du produit avec lequel nous intégrons, c'est pourquoi les seules entrées que je peux donner au processus de construction sont une liste de fichiers dans l'ordre dans lequel ils doivent être intégrés. S'il y a une erreur de compilateur, l'outil de construction échoue, nous devons soumettre manuellement une demande pour une nouvelle construction. Une liste de fichiers dans l'ordre dans lequel ils doivent être compilés est donc importante.

EDIT:

trouvé ceci: http://www.oracle.com/technology/oramag/code/tips2004/091304.html me donne les dépendances de tout objet. Maintenant, j'ai juste besoin d'avoir le bon ordre ... Si je trouve quelque chose qui fonctionne, je l'afficherai ici.

EDIT: (! Avec le code)

Je sais qu'en général, ce genre de chose n'est pas nécessaire pour Oracle, mais pour tous ceux qui est toujours intéressé ...

Je bricolé un petit script qui semble être en mesure d'obtenir un ordre de construction tels que tous les paquets seront construits dans l'ordre correct sans erreur liés à la dépendance (par rapport à pacakges) la première fois:

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; 

Je sais ce n'est pas le plus joli code (globals partout, etc ...), et je vais probablement le rediffuser si je peux avoir une chance cet après-midi de le nettoyer, mais maintenant, il produit un ordre de build qui semble courir la première fois sans problèmes.

:OBJECT_NAME doit être l'objet racine dont vous souhaitez suivre toutes les dépendances et l'ordre de génération. Pour moi, c'est un paquet principal avec une seule méthode qui est le point d'entrée dans le reste du système.

J'ai principalement limité à PACKAGE BODY, mais il ne devrait pas être trop de travail pour inclure d'autres types, tels que les déclencheurs.Une dernière chose, l'objet spécifié par :OBJECT_NAME n'apparaîtra pas dans la sortie, mais il devrait être le dernier élément, donc vous devrez l'ajouter manuellement à votre liste de construction.

MISE À JOUR: Je viens de découvrir user_dependencies et all_dependencies, ce code pourrait probablement être beaucoup plus simple maintenant.

+0

voulez-vous dire Oracle packages PL/SQL? –

+0

La seule façon de le faire est de le scripter et de tester le déploiement sur un nouveau schéma. Réorganiser les paquets dans le script selon les besoins en fonction des erreurs ... –

+0

Oui, je voulais dire des paquets. – FrustratedWithFormsDesigner

Répondre

-1

Solution actuelle: Le script ci-dessus semble indiquer l'ordre de construction correct. Pourrait probablement être réécrit "plus agréable", mais je vais laisser cela comme un exercice pour le lecteur. ;)

Après discussion, l'outil de construction interprétera n (4, en fait) builds de suite avant de signaler les erreurs. Cela aiderait aussi à résoudre les erreurs de compilation des dépendances si l'ordre de construction est faux, mais je préférerais juste obtenir l'ordre de construction correct la première fois.

2

Regardez le script suivant de 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; 
/
+0

Intéressant, mais je ne suis pas sûr que cela fournira un ordre de construction réel. – FrustratedWithFormsDesigner

0

Vous n'avez pas besoin d'un ordre de construction - juste construire les paquets avec « CREATE OR REPLACE ... » sur une base fichier par fichier, puis les compiler dans une boucle imbriquée à deux niveaux - chaque passage dans la boucle interne compile tout ce qui est encore invalide et la boucle externe est utilisée pour vérifier le nombre d'objets invalides restants et définir une sorte de seuil pour les exécutions maximales de la boucle interne. En pratique, je n'ai jamais vu le nombre de passes nécessaires être supérieur à trois. Si vous avez plusieurs schémas impliqués dans les dépendances, regardez dans l'exécution du script Oracles utlrp.sql, qui fonctionne à travers les schémas et met en place une infrastructure pour gérer le processus - cela nécessite cependant un compte privilégié. De même, si vous étendez le contrôle de votre source pour inclure des vues, assurez-vous que les scripts utilisent "CREATE OR REPLACE FORCE VIEW ..." pour créer des vues qui ont des dépendances non satisfaites au moment de leur création.

Exemple de script que j'utilise:

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;  
/
8

Si vous êtes vraiment affaire avec seulement des paquets PL/SQL vous n'avez pas besoin de transpirer l'ordre de construction. Construisez simplement toutes les spécifications du paquet en premier. Ensuite, vous pouvez déployer tous les corps de paquetages et ils vont compiler, car leurs dépendances sont les spécifications du paquet. Si vous avez des spécifications de paquet qui dépendent d'autres spécifications - si vous avez des paquets qui déclarent, par exemple, des constantes, des sous-types ou des curseurs ref qui sont utilisés dans les signatures de procédures empaquetées - alors vous devez construire ces package specs en premier. Mais il devrait y en avoir assez peu pour que vous puissiez les organiser dans le script de construction à la main.

modifier

On dirait qu'ils wil être fait progressive et « nettoyage balayage » construit, donc l'ordre de construction sera question plus quand ils nettoient l'environnement et reconstruire .

Cela ne change rien.

Voici un exemple étendu. J'ai un schéma avec trois paquets ....

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> 

La chose intéressante est qu'une procédure Pkg1 appelle une procédure de PKG2, une procédure en PKG2 appelle une procédure de PKG3 et une procédure en PKG3 appelle une procédure de PKG1.

Q. Comment cette dépendance circulaire fonctionne-t-elle?
A. Ce n'est pas une dépendance circulaire ....

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> 

sont les corps de l'emballage Tous les objets dépendants, tous les objets référencés sont les spécifications emballés. Par conséquent, si je trash'n'rebuild le schéma, peu importe quel ordre j'utilise. D'abord, nous détruisons ...

SQL> drop package pkg1 
    2/

Package dropped. 

SQL> drop package pkg2 
    2/

Package dropped. 

SQL> drop package pkg3 
    2/

Package dropped. 

SQL> 

Ensuite, nous Reconstruire ...

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> 

L'ordre des objets individuels est hors de propos. Construisez simplement les spécifications du paquet avant les corps du paquet. Bien que même cela ne compte pas vraiment ...

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 est VALIDE parce que nous n'avons pas encore construit 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> 

Tout construit en utilisant CREATE OR REPLACE est toujours créé, il est juste marqué comme VALIDE s'il y a des erreurs. Dès que nous l'exécutons, directement ou indirectement, la base de données le compile pour nous. Donc, l'ordre n'a pas d'importance. Vraiment pas. Si l'idée de terminer une construction avec des objets invalides vous préoccupe - et j'ai de la sympathie pour cela, on nous dit de ne pas vivre avec des fenêtres brisées - vous pouvez utiliser le script utlrp ou dans 11g the UTL_RECOMP package; chaque approche nécessite un compte SYSDBA.

modifier 2

Le processus est basé autour d'une construction outil qui a été construit par le vendeur de le produit que nous intégrons avec ce qui explique pourquoi les seules entrées que je peux donner le processus de construction sont une liste de fichiers dans l'ordre dont ils doivent être intégré. S'il y a une erreur compilateur , l'outil de construction échoue, nous avons pour soumettre manuellement une demande pour une nouvelle construction .

Ceci est un problème politique et non technique. Ce qui ne veut pas dire que les problèmes politiques ne peuvent pas être résolus avec une solution technique, mais que la solution technique n'est pas le meilleur outil pour le travail. Bonne chance.

+0

J'ai commencé à le faire à la main et je me suis rendu compte que nous devrons continuer à le faire à mesure que le projet progressera et que de nouveaux paquets seront ajoutés (et il y aura de nouveaux paquets, probablement à la fin du printemps). puis). – FrustratedWithFormsDesigner

+0

Par tous les moyens automatiser la construction. Mais si vous ajoutez un paquet maintenant, vous avez juste besoin de l'ajouter à la fin du script de construction. L'ordre de dépendance n'a vraiment aucune importance avec les paquets. – APC

+0

S'ils construisaient le code progressivement, je pense que vous auriez raison. On dirait qu'ils vont faire des builds incrémentaux ET "clean-sweep", donc l'ordre de construction aura plus d'importance quand ils nettoieront l'environnement et le reconstruiront. – FrustratedWithFormsDesigner

0

Ajoutez la commande suivante en haut de votre script:

SET VERIFY OFF

cela permettra à vos scripts pour exécuter sans validation et peut donc être exécuté dans un ordre quelconque.

Vous pouvez interroger ultérieurement DBA_ERRORS pour obtenir toutes les erreurs et tous les avertissements dans vos packages, vues et types.

1

Une petite chose à surveiller lorsque vous marchez dans l'arborescence des dépendances. Les dépendances pour les programmes non compilés ne s'affichent pas ...

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> 

Ainsi, le corps pour PKG4 est invalide parce ZZZ_CONSTANTS_PKG n'existe pas.

SQL> create or replace package zzz_constants_pkg is 
    2  whatever constant varchar2(20) := 'WHATEVER'; 
    3 end zzz_constants_pkg; 
    4/

Package created. 

SQL> 

Mais le corps pour PKG4 est encore VALIDE si la requête suivante ne retourne pas sa dépendance à l'égard 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> 

Maintenant Compilons PKG4 et re-interroger les dépendances ...

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

Essayez ceci à la place de 11.1 et plus. Exécutez le script dans n'importe quel ordre. A l'issue de la fin de la commande suivante: (Modifier les paramètres de commande en fonction de vos besoins)

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

Plus de détails sur DBMS_UTILITY.compile_scema

Questions connexes