E16 - Diseño de Sistemas de Bases de Datos Boletín 3 Nombre:_____________________________________________________________________________________________ 1. Esta práctica está dedicada a la creación de disparadores sobre sentencias DDL (lenguaje de definición de datos) y sobre eventos de la Base de Datos. También veremos la ejecución de trabajos de forma periódica. A continuación se detallan los eventos que pueden se capturados, así como los procedimientos del paquete DBMS_JOB: Sobre sentencias DDL ALTER ANALYZE ASSOCIATE STATISTICS AUDIT COMMENT CREATE DISASSOCIATE STATISTICS DROP GRANT NOAUDIT RENAME REVOKE TRUNCATE DDL Sobre eventos de la BD SERVERERROR LOGON LOGOFF STARTUP SHUTDOWN SUSPEND DBMS_JOB SUBMIT REMOVE CHANGE WHAT NEXT_DATE INSTANCE INTERVAL BROKEN RUN USER_EXPORT La documentación en línea con todos los detalles puede ser consultada en http://anubis/~oracle/manuales/ 2. Esta sesión se realizará sobre Oracle9i. Para acceder a esta versión hay que entrar en SQL*Plus con el siguiente nombre de usuario, sustituyendo las X por el número correspondiente: alXXXXXX/alXXXXXX@pc Es necesario volver a crear las tablas utilizadas en el Boletín 1, ejecutando SQL> start /home/inf/dba/PUBLIC/E16/crea_tablas.sql 3. Vamos a empezar definiendo 2 disparadores para poder controlar quien se conecta a la base de datos y cuánto tiempo permanece conectado. Para ello, crearemos un disparador sobre el evento LOGON y otro sobre LOGOFF que actualice la fila del LOGON correspondiente. Para realizar este punto, teclearemos el siguiente código en un fichero llamado trg_bd_1.sql create or replace trigger al000000_01 after logon on schema begin insert into p_logins (owner, f_inicio, ip, session_id) values (ora_login_user, sysdate, nvl(ora_client_ip_address,'Desconocida'), dbms_session.unique_session_id); end; / create or replace trigger al000000_02 before logoff on schema begin update p_logins set f_fin = sysdate where owner = ora_login_user and session_id = dbms_session.unique_session_id; end; / Una vez creado el fichero, lo ejecutaremos desde el prompt del SQL*Plus: SQL> start trg_bd_1 4. Una vez creados los 2 disparadores, procedemos a realizar las correspondientes pruebas: SQL> connect alXXXXXX/********@pc SQL> connect alXXXXXX/********@pc Cada connect implica el cierre de la conexión anterior. Para ver si realmente se ha almacenado las conexiones, realizaremos la consulta siguiente: select owner, to_char(f_inicio, 'dd-mm-yyyy hh24:mi:ss'), to_char(f_fin, 'dd-mm-yyyy hh24:mi:ss') from p_logins; ¿Se obtiene la información esperada?........................................................................................................................................ ................................................................................................................................................................................................... ................................................................................................................................................................................................... 5. El siguiente disparador que vamos a definir, va a permitir registrar todos los errores que produzcamos. Para ello lo dispararemos sobre el evento SERVERERROR y restringido a nuestro esquema (SCHEMA). Para realizar este punto, teclearemos el siguiente código en un fichero llamado trg_bd_2.sql create or replace trigger al000000_03 after servererror on schema begin insert into p_errores (fecha, error_texto) values (sysdate, dbms_utility.format_error_stack); end; / Una vez creado el fichero, lo ejecutaremos desde el prompt del sqlplus: SQL> start trg_bd_2 6. Para realizar las pruebas sobre el disparador anterior, podemos lanzar alguna sentencia simple, o incluso alguna sentencia de la sesión anterior. desc patata drop table patata; insert into p_puntuaciones (usu_usuario, ite_id, puntuacion) values (‘al000000’, 3, 7); ¿Qué consulta haríamos para comprobar su funcionamiento?................................................................................................... ................................................................................................................................................................................................... ................................................................................................................................................................................................... 7. A continuación, vamos a proceder a la creación de un trabajo que guardaremos en la cola de trabajos. Este trabajo, lanzará un procedimiento, que conectándose a anubis, nos mandará un e-mail con un listado de las conexiones que se han realizado. El procedimiento se encuentra en un fichero que hay que ejecutar: SQL> start /home/inf/dba/PUBLIC/E16/crea_proc_mail.sql 8. Una vez creado el procedimiento, teclearemos el bloque PL/SQL siguiente. El trabajo almacenado se ejecutará según el campo NEXT_DATE (tercer parámetro), y se ejecutará periódicamente según formula definida en INTERVAL (cuarto parámetro). declare job number; begin dbms_job.submit(job, 'manda_mail;', sysdate, 'sysdate + 1/(24*12)'); commit; end; / ¿Se ha ejecutado el trabajo?....................................................................................................................................................... ................................................................................................................................................................................................... ................................................................................................................................................................................................... 9. Para comprobar que se ha almacenado correctamente y cuales son sus parámetros, podemos realizar la siguiente consulta: select job, to_char(next_date,'dd-mm-yyy hh24:mi:ss'), interval, what from user_jobs; ¿Se obtiene la fila con los datos previstos?................................................................................................................................ ................................................................................................................................................................................................... ................................................................................................................................................................................................... 10. El trabajo que hemos creado, se ejecutará a intervalos de 5 minutos, lo cual puede ser un poco engorroso, por lo que procederemos a modificar su frecuencia de disparo para situarla a sólo una vez al día. El <número de job> que hay que indicar, es el obtenido en la consulta anterior (JOB). begin dbms_job.interval(<número de job>, 'trunc(sysdate) + 1'); end; / ¿Sabrías eliminar el trabajo de la cola? ..................................................................................................................................... ................................................................................................................................................................................................... ................................................................................................................................................................................................... ANEXO A ANEXO B: DBMS_JOB.SUBMIT ( job OUT BINARY_INTEGER, what IN VARCHAR2, next_date IN DATE DEFAULT sysdate, interval IN VARCHAR2 DEFAULT 'null', no_parse IN BOOLEAN DEFAULT FALSE, instance IN BINARY_INTEGER DEFAULT any_instance, force IN BOOLEAN DEFAULT FALSE); DBMS_JOB.INTERVAL ( job IN BINARY_INTEGER, interval IN VARCHAR2); DBMS_JOB.REMOVE ( job IN BINARY_INTEGER); ANEXO C create or replace procedure manda_mail is conn len res usuario cuerpo utl_tcp.connection; number(6); varchar2(3); varchar2(30); varchar2(2000); cursor cur is select rpad(owner,20)|| rpad('Conexiones: '||to_char(count(*)),30)|| 'Minutos: '||to_char(round(sum((f_fin-f_inicio)*60*24))) linea from p_logins group by owner; begin select user into usuario from dual; cuerpo := ''; for r in cur loop cuerpo := cuerpo || r.linea || chr(10); end loop; cuerpo := cuerpo || '.' || chr(10); conn := utl_tcp.open_connection ('anubis.uji.es', 25); len := utl_tcp.write_line(conn, 'HELO anubis.uji.es'); res := substr(utl_tcp.get_line(conn),1,3); len := utl_tcp.write_line(conn, 'MAIL FROM: job_oracle@anubis.uji.es'); res := substr(utl_tcp.get_line(conn),1,3); len := utl_tcp.write_line(conn, 'RCPT TO: '||usuario||'@alumail.uji.es'); res := substr(utl_tcp.get_line(conn),1,3); len := utl_tcp.write_line(conn, 'DATA'); res := substr(utl_tcp.get_line(conn),1,3); len := utl_tcp.write_line(conn, cuerpo); res := substr(utl_tcp.get_line(conn),1,3); len := utl_tcp.write_line(conn, 'QUIT'); res := substr(utl_tcp.get_line(conn),1,3); utl_tcp.close_connection(conn); end; /