4 septiembre, 2013
iSeries DB2 y RPG. Como usar Triggers o desencadenantes.
Un Trigger o desencadenante de una Base de Datos no es más que un proceso que se dispara ante algún evento configurado que ocurra en nuestro sistema. Básicamente, es una manera de decirle al sistema que cuando un registro de la Base de datos cambie, haga una llamada a un programa que nosotros especificamos y con el que podemos tratar los datos del registro modificado.
En el iSeries, la gestión de los triggers la tenemos dentro del menú CMDTRG, donde encontramos opciones para añadir un trigger, modificarlo, borrarlo o listar todos los triggers que estén definidos contra los ficheros de un biblioteca concreta. Hoy nos vamos a centrar en añadir triggers, las demás opciones son muy fácil de entender y realizar, así que las obviaré.
Para añadir un trigger basta usar la opción 1 del menú anterior o el mandato ADDPFTRG (añadir trigger de archivo físico). La configuración es bien sencilla, nos pregunta el archivo/biblioteca que queremos controlar, si el trigger se dispara antes o después de actualizar el registro, que evento lo desencadena (lectura, modificación, adición o borrado) y a que programa/biblioteca llamamos cuando se produzca el evento.
Hay que añadir un trigger por cada evento, no existe un *ALL, de manera que añadimos 1 para *INSERT, otro para *DELETE, etc….. Aunque el programa puede ser el mismo ya que podemos controlar que evento lo ha llamado.
Antes de activar un trigger debemos tener el programa generado, ya que si no la llamada nos dará error cada vez que alguien modifique un registro del archivo.
Veamos ahora como se diseña un programa llamado desde un trigger. Lo primero que tenemos que tener en cuenta son los parámetros de entrada. Todos los trigger llaman al programa indicado usando los mismos parámetros y que detallo a continuación….
D PARM1 DS 9999 D FNAME 1 10 *Nombre del Archivo que dispara el trigger D LNAME 11 20 * Bliblioteca del Archivo D MNAME 21 30 * Miembro del Archivo</pre> D TEVEN 31 31 * Evento Activador (1, 2 o 3) * '1' OPERACIÓN DE INSERCIÓN * '2' OPERACIÓN DE SUPRESIÓN * '3' OPERACIÓN DE ACTUALIZACIÓN D TTIME 32 32 * Hora del Activador * '1' DESPUÉS DE LA OPERACIÓN * '2' ANTES DE LA OPERACIÓN D CMTLCK 33 33 * Nivel de Bloqueo de Compromiso D FILL1 34 36 * Reservado D CCSID 37 40B 0 * CCSID D FILL2 41 48 * Reservado D OLDOFF 49 52B 0 * Desplazamiento al Reg Original D OLDLEN 53 56B 0 * Longitud del Registro Original D ONOFF 57 60B 0 * Desplazamiento Bytes Nulos Reg Original D ONLEN 61 64B 0 * Longitud de Correlacion de Bytes Nulos D NOFF 65 68B 0 * Desplazamiento al nuevo registro D NEWLEN 69 72B 0 * Longitud Nuevo Registro D NNOFF 73 76B 0 * Desplazamiento Bytes Nulos Reg Nuevo D NNLEN 77 80B 0 * Longitud de Correlacion de Bytes Nulos D RESV3 81 96 * Reservado * D PARM2 DS D LENG 1 4B 0 *---------------------------------------------------------------* C *ENTRY PLIST C PARM PARM1 C PARM PARM2
Con estos parámetros tenemos toda la información que necesitamos para actuar en caso de que se dispare un trigger, tenemos el archivo, miembro y biblioteca que estamos tratando, el evento que se está produciendo, como estaba el registro antes del evento y como está después de ocurrir.
Bien, pues ahora vamos a ver como tratamos esta información.
Yo, normalmete uso el mismo programa para un mismo grupo de archivos, por ejemplo todos los que tengan que ver con artículos, de esta manera simplifico el número de programas y lo tengo más controlado. El programa ILERPG que creemos debe empezar con las descripciones y parametros de entrada que hemos descrito antes, y a partir de ahí, podemos tratar la información como queramos, como en cualquier otro programa.
Primero deberíamos definir como estructuras de datos las definiciones de los registros de los archivos que vamos a tratar, para simplificar el uso de campos definidos externamente. Esto nos ahorra bastante código que escribir.
D REGART E DS EXTNAME(ARTICU) D REGATR E DS EXTNAME(ARTATR)
Luego utilizaremos una sentencia SELECT para distinguir que fichero es el que estamos procesando, y dentro de él, extraeremos los datos del registro anterior y del registro actual.
Primero controlamos el evento con el campo TEVEN, si no es 2, es decir, no es borrado, recuperamos el registro nuevo a partir del parametro PARM1 con el inicio y la longitud que nos proporcionan. Una vez recuperada la cadena, la movemos a la estructura del registro para poder acceder a los campos del mismo.
Si el parametro TEVEN es 2, o sea, el evento desencadenante es un borrado, el campo de registro nuevo viene en blanco, por lo que debemos recuperar el registro anterior para saber que datos hemos suprimido. Obviamente, por la misma regla, cuando sea un 1 correspondiente al alta, no existe el registro anterior.
C SELECT *--------------------------------------------------------------* C FNAME WHENEQ 'ARTICU' * C TEVEN IFNE '2' * Recuperamos el registro Nuevo y lo dejamos en la estructura REGART C Z-ADD NOFF X 5 0 C ADD 1 X C Z-ADD NEWLEN Y 5 0 C Y SUBST PARM1:X REGART * Llamada al programa de actualización de archivos remotos C CALL 'ACTARTCL' C PARM TEVEN C PARM REGArt C ENDIF * C ELSE * Recuperamos los datos del Registro eliminado C Z-ADD OLDOFF X 5 0 C ADD 1 X C Z-ADD OLDLEN Y 5 0 C Y SUBST PARM1:X REGArt * Llamada al programa de actualización de archivos remotos C CALL 'ACTARTCL' C PARM TEVEN C PARM REGArt * C ENDIF *--------------------------------------------------------------* C FNAME WHENEQ 'ARTATR' *................... C ENDSL *---------------------------------------------------------------*········· C MOVEL *ON *INLR
Yo normalmente con los datos recuperados hago una llamada a otro programa que se ejecuta en Batch, de manera que si se produce algún error en el programa, el usuario no es interrumpido y lo controlo en la cola de trabajos correspondiente.
Debéis tener en cuenta que si un trigger está definido como que se ejecute con el valor *BEFORE, el cambio todavía no se ha producido en el archivo, por lo que tened cuidado con las pérdidas de información. Yo suelo usar siempre *AFTER, de esa manera me aseguro de que los datos siempre están actualizados.
Pero esto son consejos míos, el caso es que con toda esta información, ya podemos controlar lo que ha sucedido en el momento de dispararse el trigger. Incluso en el caso de una modificación, recuperar el registro antiguo y el nuevo y saber exactamente que campos se han modificado. A partir de aquí, lo que cada uno quiera o necesite hacer con ese registro.
Muchas gracias. fue de gran ayuda,
Todo perfecto, pero si además se necesita captura el usuario que al acceder a la tabla hizo que el trigger se ejecute, ¿como lo hago?
Pues si no me equivoco el trabajo que se ejecuta con el trigger se hace con el mismo usuario por lo que puedes recuperarlo con QSYS/RTVJOBA USER(&USER)
Buenos dias, puedo tener varios triggers, para la misma tabla, con el mismo evento (insert, update, etc) y con el mismo Trigger time (*before, *after)? Pero que llame a diferentes programas, que ejecuten cosas diferentes? Gracias de antemano. Saludos.
Buenos días
Puedes perfectamente tener varios triggers que se ejecuten en el mismo momento y llamen a programas diferentes. Solo ten en cuenta que al crearlos no cambies la opción sustituir desencadenante a *YES, eso haría que eliminase el anterior. Si lo dejas en *NO tendrás ambos triggers.
Para comprobar los triggers que tienes activos puedes usar el mandato PRTTRGPGM
Saludos
Hola, puedo identificar el nombre del programa que está realizando la actualización del archivo? gracias!!
Hola, pues realmente creo que no.
en los parámetros que recibe el programa desencadenante vienen todo lo relativo al registro modificado, pero nada de quién lo modifica.
en esta cuestión no puedo ayudarte.
Accesando la pila de llamadas puedes obtener el nombre del programa que generó el evento
Si, es una opción, pero se sale del trigger como tal.
Tendrías que tener un programa que chequeara la pila de llamadas y ver cual es el último.
Hola,
Creé el programa del trigger en RPG ILE, la tabla la cree en DB2, está tabla tiene campos tanto carácter como decimal, en el primer parámetro del programa del trigger donde llega la información de la tabla solo me está mostrando bien los campos carácter, pero los campos decimales me los muestra con caracteres raros de esos que no son parte de nuestro teclado, me ayuda por favor, sabe por qué se puede dar este caso y como solucionarlo?
Hola, pues eso suele ocurrir porque el dato numérico en un archivo está empaquetado y su representación no se puede pasar a texto tal cual.
Si ves en el ejemplo, yo creo un área de datos igual al registro del fichero
D REGART E DS EXTNAME(ARTICU)
Y luego recupero los datos del registro y lo muevo a este área. Entonces se convierten automáticamente los datos a su formato concreto.
Depende del registro que quieras recuperar usas este código para extraer la cadena del triggers y moverla al registro.
* Recuperamos el registro Nuevo y lo dejamos en la estructura REGART
C Z-ADD NOFF X 5 0
C ADD 1 X
C Z-ADD NEWLEN Y 5 0
C Y SUBST PARM1:X REGART
Espero haberte servido de ayuda.
Saludos