sábado, 17 de abril de 2010

Forms6: Cerrar Report Background por código

Hace tiempo comenté aquí la forma de cerrar el Report Background Engine cada vez que se ejecuta un informe Forms6: Forzar cierre report background, en ese momento comenté que no me parecía muy optimo el cierre del Report Background Engine cada vez que se lanza un informe, pero alguna vez se me dio la necesidad de hacerlo por dos motivos:

  • El usuario tenía que ejecutar dos programas en Forms con distinta configuración de NLS_LANG, uno con SPANISH_SPAIN.UTF8 y otro con SPANISH_SPAIN.WE8ISO8859P15, de forma que al ejecutar un informe se abría el Report Background con la configuración de NLS_LANG del programa que lo llamaba y el otro programa al tener abierto el Report Background no cogía bien la configuración del NLS_LANG, en ese caso lo mejor es forzar que se cierre el Report Background por cada informe.
  • Conexiones remotas por Terminal Server con un programa publicado en Forms. Al lanzar un informe se abre el Report Background y queda abierto y al salir del programa se mantiene la conexión de Terminal Server por culpa del Report Background cuando en realidad ya no va a ser utilizado, en ese caso lo mejor sería forzar cerrar el Report Background al salir del programa, para ello podemos forzar que el Report Background se cierre ejecutando lo siguiente en el disparador KEY-EXIT del programa:
DECLARE
  pl_id ParamList; 
BEGIN 
  pl_id := Get_Parameter_List('CERRAR_RBE'); 

  IF NOT Id_Null(pl_id) THEN 
    Destroy_Parameter_List( pl_id ); 
  END IF; 

  pl_id := Create_Parameter_List('CERRAR_RBE'); 

  Add_Parameter(pl_id, 'PARAMFORM', TEXT_PARAMETER, 'NO');
  Add_Parameter(pl_id, 'ORACLE_SHUTDOWN', TEXT_PARAMETER, 'Yes');

  Run_Product(REPORTS, 'cerrar_rbe', SYNCHRONOUS, RUNTIME, FILESYSTEM, pl_id, NULL);
  Destroy_Parameter_List( pl_id );
END;

El informe 'cerrar_rbe' no es necesario que exista, simplemente se intenta ejecutar un informe pasándole el parámetro para forzar el cierre del Report Background.

martes, 19 de enero de 2010

¿Las claves foráneas producen bloqueos en Forms?

La respuesta es que si no hacemos bien las cosas es SI. Vamos a verlo mejor con un ejemplo con las típicas tablas de formación que utiliza Oracle,  DEPT y EMP:

CREATE TABLE dept (
  deptno  NUMBER(2) NOT NULL CONSTRAINT dept_pk PRIMARY KEY,
  dname   VARCHAR2(14),
  loc     VARCHAR2(13));

CREATE TABLE emp (
  empno    NUMBER(4) NOT NULL CONSTRAINT emp_pk PRIMARY KEY,
  ename    VARCHAR2(10),
  job      VARCHAR2(9),
  mgr      NUMBER(4),
  hiredate DATE,
  sal      NUMBER(7,2),
  comm     NUMBER(7,2),
  deptno   NUMBER(2) CONSTRAINT emp_ref_dept_fk
                     REFERENCES dept(deptno));


Insertamos un departamento:

INSERT INTO dept (deptno, dname, loc)
  VALUES (23, 'CONTABILIDAD', 'VIGO');

COMMIT;


Ahora insertamos un empleado:

INSERT INTO emp (empno,ename,job,mgr,
                 hiredate,
                 sal,comm,deptno)
  VALUES (1234, 'MARIOBROS', 'FONTA', 10,
          TO_DATE('16/05/1975', 'DD/MM/YYYY'),
          50000, 2000, 23);


Antes de hacer el commit, al ver los bloqueos veremos que están la tabla EMP y la DEPT bloqueadas.

SELECT lk.sid, lk.TYPE lock_type, lk.lmode,
       (SELECT o.object_name
          FROM dba_objects o
         WHERE o.object_id = lk.id1) object_name
  FROM gv$lock lk
 WHERE lk.TYPE IN ('TM', 'UL');


Lo que nos devolverá será esto:

   SID LO     LMODE OBJECT_NAME
------ -- --------- ----------------
   311 TM         3 EMP
   311 TM         3 DEPT


El bloqueo de la tabla EMP es por el registro que acabamos de insertar pero que todavía no está commitado, pero la tabla DEPT se bloquea por la clave foránea, ya que en el momento de insertar el registro en EMP existía el departamento 23 en DEPT, pero como no hemos comitado el insert en EMP tiene que bloquear el registro del departamento 23 para evitar que otra sesión borre ese registro o modifique el código del departamento.

Hasta aquí todo normal, el problema viene cuando otra sesión quiere modificar el nombre del departamento:

UPDATE dept
   SET dname = 'FINANCIERO'
 WHERE deptno = 23;


Con esta update nos dejará modificarlo sin problema, pero si en vez de hacer un update que únicamente modifica el nombre del departamento hacemos este otro (el resultado final de los datos en la tabla serán los mismos):

UPDATE dept
   SET deptno = 23,
       dname = 'FINANCIERO',
       loc = 'VIGO'
 WHERE deptno = 23;


El registro quedaría con los mismos datos, pero ya tenemos el bloqueo montado ya que le estamos diciendo que actualice el campo DEPTNO, pero la modificación de ese campo afectaría a registros introducidos por otra sesión y que todavía no están comitados, por lo que se quedará bloqueado.

Esta es la explicación del motivo, pero ¿porqué ocurre en Forms?. La respuesta la encontramos en la propiedad "Actualizar Sólo Columnas Cambiadas" a nivel de bloque, por defecto esa propiedad está a "No" con lo que para hacer un update actualizará todos los campos, incluso los que no ha modificado el usuario.

Lo recomendable para evitar bloqueos es poner esa propiedad al valor "Si".