Anuncios en tutorial de programación PLSQL

martes, 4 de junio de 2019

Cláusula BEQUEATH para las vistas (Oracle 12c)

Anteriormente a la versión 12c de las bases de datos Oracle, si desde una vista había que ejecutar una función PL/SQL siempre se invocaba con permisos del propietario de la vista, no los privilegios del propietario de la función. Esto implicaba que si la función había sido definida por el invocador, la conducta de la misma podía ser bastante diferente a lo esperado cuando se ejecutaba desde una vista.


Para solucionar este problema, la versión 12c de la base de datos Oracle incorpora la cláusula BEQUEATH que permite definir una vista para que si esta incluye funciones, los permisos de ejecución de las mismas se acomoden con los permisos del usuario que invoca la vista.

Para facilitar el uso de esta nueva cláusula, Oracle 12c incluye dos nuevas funciones que permiten identificar al usuario que invoca una vista (o sentencia SQL), determinando por tanto si son utilizados los permisos del usuario que la invoca o si se utilizan los del propietario de la vista:
  • Función ORA_INVOKING_USERID: devuelve el ID del usuario que está invocando la sentencia o vista actual. En el caso de que dicho usuario haya sido definido a través de la funcionalidad “Real Application Security” disponible en las bases de datos Oracle, la función devolverá un ID que es común para todas las sesiones abiertas por usuarios de este tipo pero que es diferente del ID de cualquier usuario de la base de datos.
  • Función ORA_INVOKING_USER: devuelve el nombre del usuario que está invocando la sentencia o vista actual. En el caso de que dicho usuario haya sido definido a través de la funcionalidad “Real Application Security” disponible en las bases de datos Oracle, la función devolverá el valor XS$NULL.

Las funciones antes mencionadas solo pueden invocarse desde una sentencia SQL, es decir, no están disponibles desde PL/SQL nativo.

Veamos a continuación con un ejemplo como funciona la cláusula BEQUEATH y las funciones antes mencionadas. A continuación en el esquema INV crearemos la tabla articulos_inventario y la función cuenta_articulos que llama a las funciones ORA_INVOKING.
CREATE TABLE inv.articulos_inventario
(
  articulo_id   INTEGER,
  almacen_id    INTEGER,
  articulo      VARCHAR2 (100)
)
/

BEGIN
  INSERT INTO inv.articulos_inventario
    VALUES   (1, 10, 'UVW');

  INSERT INTO inv.articulos_inventario
    VALUES   (2, 10, 'XYZ');

  INSERT INTO inv.articulos_inventario
    VALUES   (3, 20, '987');

  COMMIT;
END;
/

CREATE OR REPLACE FUNCTION inv.cuenta_articulos (p_almacen_id IN INTEGER)
  RETURN PLS_INTEGER
  AUTHID CURRENT_USER
IS
  l_cont         PLS_INTEGER;
  l_usuario      VARCHAR2 (100);
  l_usuario_id   VARCHAR2 (100);
BEGIN
  SELECT   COUNT (*)
    INTO   l_cont
    FROM   articulos_inventario
   WHERE   almacen_id = p_almacen_id;

  /* Invocando las funciones ORA_INVOKING */
  SELECT   ora_invoking_user INTO l_usuario FROM DUAL;
  SELECT   ora_invoking_userid INTO l_usuario_id FROM DUAL;

  DBMS_OUTPUT.put_line (l_usuario);
  DBMS_OUTPUT.put_line (l_usuario_id);
  RETURN l_cont;
END;
/

Ahora creamos la vista cuenta_articulos_v definiendo con la cláusula BEQUEATH que se ejecute utilizando los permisos del usuario que la invoca (CURRENT_USER), asegurando que el usuario APPS puede llamar a la vista.
CREATE OR REPLACE VIEW cuenta_articulos_v
  BEQUEATH CURRENT_USER
AS
  SELECT almacen_id
       , cuenta_articulos (almacen_id) 
         art_en_almacen 
    FROM articulos_inventario
/

GRANT SELECT ON cuenta_articulos_v TO apps
/

A continuación, en el esquema APPS creamos una segunda tabla articulos_inventario pero insertando datos diferentes.
CREATE TABLE apps.articulos_inventario
(
  articulo_id   INTEGER,
  almacen_id    INTEGER,
  articulo      VARCHAR2 (100)
)
/

BEGIN
  INSERT INTO apps.articulos_inventario
    VALUES   (1, 20, 'APPS.UVW');

  INSERT INTO apps.articulos_inventario
    VALUES   (2, 20, 'APPS.XYZ');

  INSERT INTO apps.articulos_inventario
    VALUES   (3, 30, 'APPS.987');

  COMMIT;
END;
/

Y finalmente ejecutamos bajo el usuario APPS un SELECT sobre la vista que hemos definido anteriormente y vemos la salida correspondiente.
SQL> SET SERVEROUTPUT ON
SQL> SELECT * FROM INV.articulos_inventario
2 /

ALMACEN_ID  ART_EN_ALMACEN
----------  --------------
        10               0
        10               0
        20               2

APPS
212
APPS
212
APPS
212

Como podéis comprobar los datos que devuelve la vista proceden de la tabla del esquema INV (hay dos registros para el almacén ID 10), pero los valores devueltos por la función cuenta_articulos reflejan los datos almacenados en la tabla del esquema APPS. Asimismo, las funciones ORA_INVOKING devuelven la información relacionada con el usuario APPS.

Debemos advertir que la cláusula BEQUEATH CURRENT_USER no transforma la vista en un objeto propio del usuario que la invoca. El nombre de la vista requiere que se utilice el nombre del esquema propietario de la misma y los permisos de ejecución sobre la misma se comprueban sobre dicho esquema.

El principal beneficio de esta funcionalidad es que posibilita que funciones como USERENV o SYS_CONTEXT devuelvan valores consistentes con las funciones PL/SQL que sean referenciadas en una vista.

Artículos relacionados: Vistas materializadas.

0 comentarios: