martes, 21 de julio de 2026

Un verdadero clásico: conocías el paquete DBMS_DB_VERSION? Compilación condicional en la base de datos Oracle


Es un paquete pequeño, pero contiene más elementos de los que normalmente se utilizan. Su propósito principal es permitir que el código PL/SQL sea compatible entre distintas versiones de Oracle mediante compilación condicional y comprobaciones de versión.

Este paquete expone constantes que permiten identificar la versión mayor, la release y comprobar características específicas de la base de datos.

Una de las razones por las que existe este paquete es permitir escribir código compatible con varias versiones de Oracle.

El paquete principalmente es llamado a través de sus constantes VERSION y RELEASE, no expone procedimientos, funciones ni parámetros, sino únicamente constantes públicas como:

Además de VERSION y RELEASE, el paquete define constantes booleanas que permiten saber si el código se está compilando para una determinada versión.

En la versión de base de datos Oracle AI Database 26ai, podemos observar en el código, como las constantes para las versiones de base de datos 11.1 hacia atrás han sido marcadas como obsoletas.

Las constantes VER_LEx, son especialmente útiles cuando el objetivo es mantener compatibilidad hacia atrás.

      LINE TEXT
---------- ------------------------------------------------------------------------------
         1 package dbms_db_version is
         2   version constant pls_integer :=
         3           23; -- RDBMS version number
         4   release constant pls_integer := 0;  -- RDBMS release number
         5
         6   /* The following boolean constants follow a naming convention. Each
         7      constant gives a name for a boolean expression. For example,
         8      ver_le_9_1  represents version <=  9 and release <= 1
         9      ver_le_10_2 represents version <= 10 and release <= 2
        10      ver_le_10   represents version <= 10
        11
        12      Code that references these boolean constants (rather than directly
        13      referencing version and release) will benefit from fine grain
        14      invalidation as the version and release values change.
        15
        16      A typical usage of these boolean constants is
        17
        18          $if dbms_db_version.ver_le_10 $then
        19            version 10 and ealier code
        20          $elsif dbms_db_version.ver_le_11 $then
        21            version 11 code
        22          $else
        23            version 12 and later code
        24          $end
        25
        26      This code structure will protect any reference to the code
        27      for version 12. It also prevents the controlling package
        28      constant dbms_db_version.ver_le_11 from being referenced
        29      when the program is compiled under version 10. A similar
        30      observation applies to version 11. This scheme works even
        31      though the static constant ver_le_11 is not defined in
        32      version 10 database because conditional compilation protects
        33      the $elsif from evaluation if the dbms_db_version.ver_le_10 is
        34      TRUE.
        35   */
        36
        37   /* Deprecate boolean constants for unsupported releases */
        38
        39   ver_le_9_1    constant boolean := FALSE;
        40   PRAGMA DEPRECATE(ver_le_9_1);
        41   ver_le_9_2    constant boolean := FALSE;
        42   PRAGMA DEPRECATE(ver_le_9_2);
        43   ver_le_9      constant boolean := FALSE;
        44   PRAGMA DEPRECATE(ver_le_9);
        45   ver_le_10_1   constant boolean := FALSE;
        46   PRAGMA DEPRECATE(ver_le_10_1);
        47   ver_le_10_2   constant boolean := FALSE;
        48   PRAGMA DEPRECATE(ver_le_10_2);
        49   ver_le_10     constant boolean := FALSE;
        50   PRAGMA DEPRECATE(ver_le_10);
        51   ver_le_11_1   constant boolean := FALSE;
        52   PRAGMA DEPRECATE(ver_le_11_1);
        53   ver_le_11_2   constant boolean := FALSE;
        54   ver_le_11     constant boolean := FALSE;
        55   ver_le_12_1   constant boolean := FALSE;
        56   ver_le_12_2   constant boolean := FALSE;
        57   ver_le_12     constant boolean := FALSE;
        58   ver_le_18     constant boolean := FALSE;
        59   ver_le_19     constant boolean := FALSE;
        60   ver_le_20     constant boolean := FALSE;
        61   ver_le_21     constant boolean := FALSE;
        62   ver_le_23     constant boolean := TRUE;
        63
        64 end dbms_db_version;

64 rows selected.

SQL>

Esta es una funcionalidad poco conocida pero que tiene más de 20 años de existencia.



[oracle@oracle-server-26ai ~]$ sqlplus /nolog

SQL*Plus: Release 23.26.2.0.0 - Production on Wed Jul 22 00:12:04 2026
Version 23.26.2.0.0

Copyright (c) 1982, 2026, Oracle.  All rights reserved.

SQL> connect / as sysdba
Connected.
SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
SQL> alter session set container=pdb1;

Session altered.

PL/SQL procedure successfully completed.

El paquete no devuelve la versión completa de la base de datos, sólo la versión base
SQL> set serveroutput on SQL> begin dbms_output.put_line( dbms_db_version.version ); end; / 23 PL/SQL procedure successfully completed.

Podemos generar una versión de la consulta ampliada para poder mostrar un poco más de
información utilizando la constante RELEASE
SQL> set serveroutput on declare begin dbms_output.put_line('Oracle Version'); dbms_output.put_line('--------------'); dbms_output.put_line( dbms_db_version.version || '.' || dbms_db_version.release ); dbms_output.put_line('Compatible: ' || dbms_db_version.version || '.' || dbms_db_version.release ); end; / Oracle Version -------------- 23.0 Compatible: 23.0
PL/SQL procedure successfully completed.

Compilación condicional
Oracle permite usar directivas especiales:
  • $IF
  • $THEN
  • $ELSE
  • $END

Estas son evaluadas por el compilador.

Por ejemplo:
SQL> DECLARE x number; BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN x := 10; $ELSE x := 20; $END DBMS_OUTPUT.PUT_LINE('Valor de x: '||x); END; / Valor de x: 10 PL/SQL procedure successfully completed.

En este caso el compilador dependiendo la versión en la cuál compiles el programa,
así será la generación del código que utilice. por eso en la ejecución del procedimiento
en la versión 26.23.2 devuelve el valor 10
Pero si ejecutamos el mismo código en una versión 19c, el resultado será 20.
SQL> select BANNER_FULL from v$version; BANNER_FULL -------------------------------------------------------------------------------- Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production Version 19.22.0.0.0 SQL> DECLARE x number; BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN x := 10; $ELSE x := 20; $END DBMS_OUTPUT.PUT_LINE('Valor de x: '||x); END; / Valor de x: 20 PL/SQL procedure successfully completed. Todo esto esta muy bien, pero el procedimiento va más allá.
Podemos utilizar las constantes boleanas, para determinar cuál es la versión de la
base de datos de tal forma que basado en el valor obtenido, ejecute la parte del código
que corresponda.
Ejecutando en Oracle AI Database 26ai.
SQL> DECLARE x NUMBER; BEGIN IF DBMS_DB_VERSION.VER_LE_19 THEN x := 10; ELSIF DBMS_DB_VERSION.VER_LE_23 THEN x := 13; ELSE x := 20; END IF; DBMS_OUTPUT.PUT_LINE('Valor de x: ' || x); END; / Valor de x: 13 PL/SQL procedure successfully completed.

Ahora veamos un ejemplo de ejecución sobre una acción real a nivel de la base de datos.
Necesitamos crear un procedimiento que sea capaz de borrar una tabla, pero que dependiendo
de la versión que sea, utilice el método con el condicionamiento IF EXISTS o simplemente un
DROP TABLE, al no existe la validación, como en versiones 19c o previas. #### Oracle 19c SQL> create table t1( x number, y char); Table created. SQL> desc t1 Name Null? Type ---------------------- -------- ---------- X NUMBER Y CHAR(1)
Al ejecutar el procedimiento con el IF EXISTS en 19c, provoca que el procedimiento genere
un error de ejecución ORA-00933
SQL> set serveroutput on SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada (si existía).'); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLERRM); END; / ORA-00933: SQL command not properly ended PL/SQL procedure successfully completed.
Ahora hagamos el mismo proceso, pero con la sintáxis aceptada en 19c.
SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada.'); EXCEPTION WHEN OTHERS THEN IF SQLCODE = -942 THEN DBMS_OUTPUT.PUT_LINE('La tabla T1 no existe.'); ELSE RAISE; END IF; END; / Tabla T1 eliminada. PL/SQL procedure successfully completed. SQL> create table t1( x number, y char); Table created.

Ahora agreguemos la opción de compilación condicional al script, para que no nos genere error
en la versión 19c. DECLARE BEGIN $IF DBMS_DB_VERSION.VERSION >= 23 $THEN EXECUTE IMMEDIATE 'DROP TABLE IF EXISTS t1'; $ELSE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END; $END DBMS_OUTPUT.PUT_LINE('Proceso finalizado.'); END; / Proceso finalizado. PL/SQL procedure successfully completed. SQL> desc t1 ERROR: ORA-04043: object t1 does not exist SQL>

En la version de Oracle AI Database 26ai, ambas instrucciones serán ejecutadas sin presentar
ningún error, ya que ambas son válidas. ##Oracle 26ai SQL> create table t1( x number, y char); Table created. SQL> DECLARE BEGIN EXECUTE IMMEDIATE 'DROP TABLE t1'; DBMS_OUTPUT.PUT_LINE('Tabla T1 eliminada.'); EXCEPTION WHEN OTHERS THEN IF SQLCODE = -942 THEN DBMS_OUTPUT.PUT_LINE('La tabla T1 no existe.'); ELSE RAISE; END IF; END; / Tabla T1 eliminada. PL/SQL procedure successfully completed.
¿Por qué Oracle diseñó este mecanismo?

Esto lo hizo pensado principalmente para fabricantes de software (ISVs) y equipos que mantienen una misma base de código para múltiples versiones de Oracle. Por ejemplo, un producto que debe soportar Oracle 19c, 21c y 23ai puede distribuir el mismo código fuente y dejar que cada base de datos compile automáticamente la variante adecuada.


El dato histórico interesante:

Aunque el paquete existe desde 9.2, su verdadero protagonismo llegó con PL/SQL Conditional Compilation, introducida en Oracle Database 10g Release 2 (10.2). Fue entonces cuando Oracle empezó a recomendar expresiones como:
$IF DBMS_DB_VERSION.VER_LE_10 $THEN
   -- Código para Oracle 10g o anterior
$ELSIF DBMS_DB_VERSION.VER_LE_11 $THEN
   -- Código para Oracle 11g
$ELSE
   -- Código para versiones posteriores
$END

Antes de 10.2 el paquete ya existía, pero no podía aprovecharse con este mecanismo de compilación condicional, que es precisamente el uso para el que fue diseñado

Esta característica recién ha cumplido 24 años de existencia. La conocías.?


No hay comentarios:

Publicar un comentario

Te agradezco tus comentarios. Te esperamos de vuelta.

Todos los Sábados a las 8:00PM