2010-02-02 91 views
7

Estoy tratando de crear una lista de dependencias de paquetes PL/SQL para poder ayudar a configurar un script de compilación automatizado para que mis paquetes se ejecuten en el servidor de prueba. ¿Hay alguna manera de comenzar con un paquete único (un paquete "raíz" identificado por nombre, idealmente) y luego encontrar todas las dependencias y el orden en el que deben compilarse? Las dependencias ya están completamente resueltas en mi esquema personal (por lo que al menos tengo un lugar donde comenzar, pero ¿a dónde voy ahora?).Ordenes de compilación Oracle y dependencias de paquetes PL/SQL

(Oracle 10,2)

EDIT:

La herramienta de construcción que se utiliza utilizará el orden de construcción y será posible recuperar esos archivos con el fin de control de código fuente, y luego se pasa a Oracle a compilar (la herramienta de compilación propiamente dicha está escrita en Python o Java, o ambas cosas, no tengo acceso a la fuente). Básicamente, la herramienta de compilación necesita como entrada una lista de archivos para compilar en el orden en que deben compilarse en, y acceder a esos archivos en control de fuente. Si tiene eso, todo funcionará bastante bien.

EDIT:

Gracias por los guiones ordenadas. Desafortunadamente, el proceso de construcción está fuera de mis manos. El proceso se basa en una herramienta de compilación construida por el proveedor del producto con el que nos estamos integrando, y es por eso que las únicas entradas que puedo dar al proceso de compilación son una lista de archivos en el orden en que deben integrarse. Si hay un error de compilación, la herramienta de compilación falla, tenemos que enviar manualmente una solicitud para una compilación nueva. Entonces, una lista de archivos en el orden en que deberían compilarse es importante.

EDIT:

encontré esto: http://www.oracle.com/technology/oramag/code/tips2004/091304.html me da las dependencias de cualquier objeto. Ahora solo necesito obtener el pedido correcto ... Si obtengo algo que funcione lo publicaré aquí.

EDITAR: (con código)

Sé que, en general, este tipo de cosas no es necesario para Oracle, pero para cualquier persona que todavía está interesado ...

he improvisado un pequeño script que parece ser capaz de obtener una orden de construcción de tal manera que todos los paquetes serán construidas en el orden correcto sin errores basadas en la dependencia (con respecto a Pacakges) de la primera vez:

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; 

que sé no es el código más bonito (globales por todos lados, etc ... ugh), y probablemente lo vuelva a publicar si puedo tener la oportunidad esta tarde de limpiarlo, pero en este momento, produce un orden de construcción que parece correr la primera vez sin problemas.

:OBJECT_NAME debe ser el objeto raíz del que desea rastrear todas las dependencias y el orden de compilación. Para mí, este es un paquete principal con un único método que es el punto de entrada al resto del sistema.

:OBJECT_TYPE Tengo restringido principalmente a PACKAGE BODY, pero no debería ser demasiado trabajo para incluir otros tipos, como desencadenantes.

Una última cosa, el objeto especificado por :OBJECT_NAME no aparecerá en la salida, pero debería ser el último elemento, por lo que deberá agregarlo manualmente a su lista de compilación.

ACTUALIZACIÓN: acabo de descubrir user_dependencies y all_dependencies, este código, probablemente se podría hacer mucho más simple ahora.

+0

¿quiere decir paquetes de Oracle PL/SQL? –

+0

La única manera que lo he hecho es guionarlo y probar la implementación en un esquema nuevo. Reordenar los paquetes en el script según sea necesario en función de los errores ... –

+0

Sí, quise decir paquetes. – FrustratedWithFormsDesigner

Respuesta

-1

Solución real: El script anterior parece dar el orden de compilación correcto. Probablemente podría ser reescrito "más bonito", pero lo dejaré como un ejercicio para el lector. ;)

Después de un análisis, la herramienta de compilación llevará a cabo n (4, en realidad) compila en una fila antes de informar los errores. Esto también ayudaría a resolver los errores de compilación de dependencias si el orden de compilación es incorrecto, pero prefiero obtener el orden de compilación correcto la primera vez.

2

mira el siguiente script desde 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

Interesante, sin embargo, no estoy seguro de que esto proporcione un orden de compilación real. – FrustratedWithFormsDesigner

0

Usted no necesita una orden de construcción - sólo construir los paquetes con "CREAR O CAMBIAR ..." sobre una base archivo por archivo y luego compilarlos en un bucle anidado de dos niveles: cada pasada en el bucle interno compila todo lo que aún no es válido y el bucle externo se utiliza para verificar el recuento de objetos inválidos restantes y establecer algún tipo de umbral para las ejecuciones máximas del bucle interno. En la práctica, nunca he visto que el número de pases necesarios sea superior a tres.

Si tiene múltiples esquemas involucrados en las dependencias, busque ejecutar el script utrac.sql de Oracles, que funciona en todos los esquemas y configura alguna infraestructura para administrar el proceso; sin embargo, esto requiere una cuenta con privilegios.

Además, si extiende su control de fuente para incluir vistas, asegúrese de que las secuencias de comandos usan "CREAR O REEMPLAZAR VISIÓN DE FUERZA ..." para crear vistas que tienen dependencias no satisfechas en el momento de su creación.

Script de ejemplo que utilizo:

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 realmente se trata de sólo paquetes PL/SQL que no es necesario que sudar la orden de construcción. Simplemente cree todas las especificaciones del paquete primero. Luego puede implementar todos los cuerpos de paquetes y compilarán, porque sus dependencias son las especificaciones del paquete.

Si tiene algunas especificaciones del paquete que dependen de otras especificaciones, si tiene paquetes que declaran, por ejemplo, constantes, subtipos o ref cursores que se utilizan en las firmas de procedimientos empaquetados, entonces necesita construir esos especificaciones del paquete primero. Pero debería haber suficientes de ellos como para que puedas organizarlos en el script de construcción a mano.

edición

Parece que bebe será haciendo incrementales y "limpieza barrido" construye, por lo que el orden de construcción importará más para cuando limpian el entorno y reconstruirlo .

Eso no altera nada.

Aquí hay un ejemplo extendido. Tengo un esquema con tres paquetes ....

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> 

Lo interesante es que un procedimiento en el PKG1 llama a un procedimiento de PKG2, un procedimiento en el PKG2 llama a un procedimiento de PKG3 y un procedimiento en el PKG3 llama a un procedimiento de PKG1.

Q. ¿Cómo funciona esa dependencia circular?
A. No es una dependencia circular ....

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> 

Todos los objetos dependientes son los cuerpos de paquetes, todos los objetos referenciados son las especificaciones envasados. En consecuencia, si trash'n'rebuild el esquema realmente no importa qué orden utilizo. En primer lugar, la basura ...

SQL> drop package pkg1 
    2/

Package dropped. 

SQL> drop package pkg2 
    2/

Package dropped. 

SQL> drop package pkg3 
    2/

Package dropped. 

SQL> 

Luego reconstrucción ...

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> 

El orden de los objetos individuales es irrelevante. Simplemente construya las especificaciones del paquete antes de los cuerpos del paquete. A pesar de que aún no importa realmente ...

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 no es válido porque no hemos construido CONSTANTS_PKG todavía.

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> 

Cualquier cosa construida usando CREATE OR REPLACE siempre se crea, se acaba marcado como no válido si hay errores. Tan pronto como lo ejecutamos, directa o indirectamente, la base de datos lo compila para nosotros. Entonces, el orden no importa. Realmente no es así.

Si la idea de terminar con una acumulación de objetos no válida preocupación que usted - y tengo cierta simpatía con eso, se nos dice no vivir con las ventanas rotas - se puede utilizar el script utlrp o en 11g the UTL_RECOMP package; cualquiera de los enfoques requiere una cuenta SYSDBA.

edición 2

El proceso se basa en una herramienta de construcción que fue construido por el vendedor del el producto estamos integrando con, por lo que las únicas entradas que pueden dar a el proceso de compilación es una lista de archivos en el orden en que deben estar integrados. Si hay un error del compilador , la herramienta de compilación falla, tenemos para enviar manualmente una solicitud para una nueva compilación .

Esto es un problema político no técnico. Lo que no quiere decir que los problemas políticos no se puedan resolver con una solución técnica, solo que la solución técnica no es la mejor herramienta para el trabajo. Buena suerte.

+0

Empecé a hacer esto a mano, y me di cuenta de que tendremos que seguir haciéndolo a mano a medida que avance el proyecto y se agreguen nuevos paquetes (y habrá nuevos paquetes, probablemente a fines de la primavera; me gustaría tener algo automatizado antes entonces). – FrustratedWithFormsDesigner

+0

Por supuesto, automatice la compilación. Pero si agrega un paquete ahora, solo necesita agregarlo al final del script de compilación. La orden de dependencia realmente no importa con los paquetes. – APC

+0

Si construyeran el código gradualmente, creo que estarías en lo cierto. Parece que harán construcciones incrementales Y de "barrido limpio", por lo que el orden de construcción será más importante para cuando limpien el entorno y lo reconstruyan. – FrustratedWithFormsDesigner

0

Añadir el siguiente comando para la parte superior de la secuencia de comandos:

SET VERIFY OFF

esto permitirá que las secuencias de comandos para ejecutar sin validación y por lo tanto se puede ejecutar en cualquier orden.

Más adelante puede consultar DBA_ERRORS para obtener todos los errores y advertencias en sus paquetes, vistas y tipos.

1

Una pequeña cosa a tener en cuenta cuando se camina por el árbol de la dependencia. Las dependencias para los programas no compilados no se muestran ...

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> 

Por lo tanto, el cuerpo de PKG4 no es válida porque ZZZ_CONSTANTS_PKG no existe.

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

Package created. 

SQL> 

Pero el cuerpo de PKG4 sigue siendo VÁLIDA por lo que la siguiente consulta no devuelve su dependencia de 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> 

Ahora vamos a compilar PKG4 y volver a consultar las dependencias ...

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

Pruebe esto en lugar de 11.1 en adelante. Ejecute el script en cualquier orden. En la edición final el siguiente comando: (Cambiar los parámetros de comando para que se adapte a sus necesidades)

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

Más detalles sobre DBMS_UTILITY.compile_scema

Cuestiones relacionadas