lunes, 24 de marzo de 2008

Compilación condicional en 9i

La compilación condicional permite indicarle al compilador los fragmentos de código que debe compilar en nuestro código PL/SQL, según las condiciones que el programador indique utilizando directivas especiales.
Ahora, si bien la compilación condicional fue presentada con Oracle 10g R2, es también posible contar con esta característica en 9i y 10g R1, gracias a la aplicación de patches sobre la base de datos.

En Oracle 10g y superiores
En Oracle 10g ya viene incorporada a partir de la versión 10.1.0.4 y activada por defecto, por lo tanto no debemos hacer nada para comenzar a utilizarla.

En Oracle 9i
Para contar con ella en 9i debemos primero tener instalado el patch 9.2.0.6 o superiores. Luego debemos setear el parámetro indocumentado _plsql_conditional_compilation=TRUE. Este parámetro es del sistema y no es dinámico, por lo que el DBA deberá incluirlo en el archivo de parámetros y reiniciar la instancia.

En Oracle 8i e inferiores
No es soportada.

Una aplicación de la compilación condicional bastante práctica y recomendable es la de hacer que nuestro código PL/SQL compile en cualquier versión, utilizando los features propios de cada release cuando es posible, todo en el mismo código. Esta buena práctica tiene dos objetivos: el primero es hacer que nuestro paquete sea reutilizable (por ejemplo un paquete de funciones de uso general), y el segundo que nuestro código no genere un impacto al momento de migrar de versión.

Un ejemplo de compilación condicional
En este ejemplo, vamos a realizar un procedimiento para hacer un spool de determinadas cuentas que cumplen con un patrón en su código.
La compilación condicional va a entrar en juego con la siguiente lógica:
-Si la base es 9i, ejecuta SQL con invocaciones a SUBSTR para verificar el patrón
-Si la base es 10g, ejecuta SQL con expresiones regulares ya que es más performante

Primero compilamos la especificación de nuestro paquete, que no tiene nada de especial:

CREATE OR REPLACE PACKAGE pack_cuentas
IS
PROCEDURE list_cuentas (p_max IN VARCHAR2);
END pack_cuentas;
/
Luego compilamos el body, donde está la diferencia:
CREATE OR REPLACE PACKAGE BODY pack_cuentas
IS
PROCEDURE spool_cuentas (p_max IN VARCHAR2)
IS
BEGIN
$IF DBMS_DB_VERSION.VER_LE_9
$THEN

FOR cue IN (SELECT cod_cuenta FROM cuentas
WHERE activo='Y' AND SUBSTR(cod_cuenta,1,2)='00'
AND SUBSTR(cod_cuenta,3,1) IN ('0','1','2','3')
AND SUBSTR(cod_cuenta,LENGTH(cod_cuenta),-1)='A')
LOOP
dbms_output.put_line(cue.cod_cuenta);
END LOOP;
$ELSE
DECLARE
v_pattern VARCHAR2(20) := '00[0-'||p_max||']-[0-9]+A';
BEGIN

FOR cue IN (SELECT cod_cuenta FROM cuentas
WHERE activo='Y' AND REGEXP_LIKE (cod_cuenta,v_pattern))
LOOP
dbms_output.put_line(cue.cod_cuenta);
END LOOP;

END;
$END
END spool_cuentas;
END pack_cuentas;
/

Las directivas especiales $IF $THEN $END hacen el truco. Observar que este mismo código se compila en Oracle 9i, donde la función REGEXP_LIKE retornaría un error de compilación, sin embargo el fragmento de código que no corresponde a la versión ni siquiera es analizado por el compilador.
Para poder diferenciar entre las versiones 9i, 9iR1, 9iR2, 10g, 10gR1 y 10gR2 contamos con el paquete DBMS_DB_VERSION, el cual incluye constantes para cada versión, como la que usé en el ejemplo. En el ejemplo usamos la constante ver_le_9 que implica 'menor o igual a 9i', pero también tenemos para indicar menor o igual a un release como por ejemplo ver_le_9_2.
CREATE OR REPLACE package dbms_db_version is
version constant pls_integer := 10; -- RDBMS version number
release constant pls_integer := 2; -- RDBMS release number
ver_le_9_1 constant boolean := FALSE;
ver_le_9_2 constant boolean := FALSE;
ver_le_9 constant boolean := FALSE;
ver_le_10_1 constant boolean := FALSE;
ver_le_10_2 constant boolean := TRUE;
ver_le_10 constant boolean := TRUE;
end dbms_db_version;
/
Ahora podremos arreglar nuestro viejo código para que sea más eficiente en las versiones siguientes y que no tengamos que hacer cambios al momento de migrar, en definitiva adelantarnos y minimizar el retrabajo en el futuro.

4 comentarios:

Unknown dijo...

Hola,

estoy empezando a trabajar con oracle 9.2 y tengo que cambiar una sentencia que esta en mysql con expresiones
regulares a oracle. La sentencia que tengo que pasar a oracle es la siguiente:

SELECT * FROM prueba WHERE (content REGEXP ?) AND (codigo = ?) AND (nombre = ?) GROUP BY mes

¿Alguien sabe como puedo pasar a oracle esta sentecia?

Muchas gracias.

Laura.

lfer dijo...

Laura,

Publiqué una nueva entrada en tu honor. Espero que te sea útil la solución.

http://oraclenotepad.blogspot.com/2010/03/expresiones-regulares-en-sql-con-9i.html

Saludos

Roberto Val dijo...

En el ejemplo la declaración de v_pattern está en el bloque de la versión 9 ($THEN), cuando debería estar en el bloque perteneciente a la versión > 9 ($ELSE). Es alli dónde se emplea la expresión regular.

Saludos.

lfer dijo...

Corregido. Gracias Roberto!
Saludos