Sunteți pe pagina 1din 4

Sacado de: http://xthefull.blogspot.com.

es/2016/08/como-auditar-cambios-usando-triggers-
en.html

Se nos ha planteado la posibilidad que indicar quién y qué es lo que se cambia en una serie de
tablas.
Sabía desde hace tiempo que el driver rdd de sixdrive permite introducir un trigger para
controlar el contenido.

Pero nosotros usamos DBFNTX, y no podemos cambiar así como así el sistema de RDD.
Por suerte para nosotros, la gente de Harbour le dio soporte también a los NTX.

Hemos diseñado un sistema de tablas, log y logid, que determina que tablas hacermos logs, y
el campo de la PK, para encontrar los cambios de un registro de una tabla determinada.

La estructura de la tabla LOG;

TABLA: Nombre de la tabla que se modificado un valor


ID_VALUE: El valor que usaremos de relación, debería ser una PK
CAMPO: El campo de la dbf se está modificando
NEW_VALUE: Nuevo dato en el dbf
OLD_VALUE: Valor que tenia.
CODUSU, IP, HOSTNAME, FECHA, HORA; Datos de control para saber quien hizo y el cambio
y desde donde.

Después , tenemos otra tabla LOGID, que determinará que TABLA vamos a auditar, en este
caso, tenemos una especie de DBU integrado que necesitamos control de todas las tablas,
porque como veremos , simplemente se activa en la apertura de la tabla si queremos que se
disparen los triggers o no,y cual es el campo de la tabla que queremos que se guarde en el
ID_VALUE de la tabla LOG.

En el ejemplo que veremos, simplemente he sustituido la tabla LOGID, por un Hash, en la


llamada a la función __GetIdLog(), en mi código, simplemente abro la tabla logid, y creo el
hash.

También, en el ejemplo, evito introducir los nuevos registros, y los campos que sean iguales,
tampoco se guardarán. Esto programarlo como queráis, la imaginación es solamente vuestra
meta ;-)

Despues, si estamos en un cliente, por ejemplo , donde la PK es el DNI y usamos el DNI como
ID a la hora de guardar el log, simplemente haciendo un SCOPE de la tabla + el DNI del
cliente, obtendremos todos los cambios aplicados a ese registro ;-)

Este ejemplo lo podéis ejecutar en el directorio de \harbour\tests, y usa la tabla test.dbf con
hbmk2 trigger.prg hbct.hbc xhb.hbc

Lo he modificado simplemente para que veais la salida a un fichero de log, audit.log

2016-08-12 12:38:53 -- trigger start --


2016-08-12 12:38:53 INFO: Ejemplo de auditar cambios a traves de triggers.
2016-08-12 12:38:53 INFO: TABLE:TEST FIELD:FIRST ID VALUE:Homer
Simpson OLD VALUE:Homer NEW
VALUE:Homer_TEST DATETIME:20160812123853989 HOSTNAME:NEO64
2016-08-12 12:38:54 INFO: TABLE:TEST2 FIELD:LAST ID VALUE: 99700 OLD
VALUE:Dysert NEW VALUE:Dysert_TESTDATETIME:20160812123854031 HOSTNAME:NEO
64
2016-08-12 12:38:54 INFO: TABLE:TEST2 FIELD:NOTES ID VALUE: 99700 OLD
VALUE:This is a test for record 500 NEW VALUE:Changes
everything DATETIME:20160812123854055 HOSTNAME:NEO64
2016-08-12 12:38:54 -- trigger end -- El resultado del ejemplo es este;

A continación el codigo fuente;

#include "Hblog.ch"
#include "dbinfo.ch"
#include "hbsix.ch"

//REQUEST DBFNTX

Function Main()

setmode( 25,80 )

rddsetdefault( 'DBFNTX' ) // Forzamos RDD por defecto de


HARBOUR

/* Activa log */
INIT LOG FILE( NIL, "audit.log", 1000, 999 ) // Tamaño a 100K y
maximo 999 ficheros
LOG "Ejemplo de auditar cambios a traves de triggers."

/* Activar triggers*/
rddInfo( RDDI_TRIGGER, "SX_DEFTRIGGER", "DBFNTX" )

/*Llamar antes de abrir la tabla que queremos controlar*/


sx_SetTrigger( TRIGGER_PENDING, "_trigger", "DBFNTX" )
USE "TEST" NEW SHARED

/* O podemos hacer de esta manera*/


USE TEST ALIAS "TEST2" NEW SHARED TRIGGER "_trigger"

Select TEST
go top
if rlock()
replace FIRST with alltrim( field->FIRST ) + "_TEST"
unlock
commit
endif

Select TEST2
go bottom
if rlock()
replace LAST with alltrim( field->LAST ) + "_TEST"
replace NOTES with "Changes everything"
unlock
endif

CLOSE LOG
CLOSE ALL

Return 0
function _trigger( nEvent, nArea, nFieldPos, xTrigVal )
Local xIdValue, xValue, cIdValue

DO CASE

CASE nEvent == EVENT_PREUSE


CASE nEvent == EVENT_POSTUSE
CASE nEvent == EVENT_UPDATE
CASE nEvent == EVENT_APPEND
CASE nEvent == EVENT_DELETE
CASE nEvent == EVENT_RECALL
CASE nEvent == EVENT_PACK
CASE nEvent == EVENT_ZAP
CASE nEvent == EVENT_PUT

if empty( cIdValue := __GetIdLog( ALIAS( nArea ) ) ) //


Si no viene expresion, no controlaremos el log
return .T.
endif
Sx_SetTrigger( TRIGGER_DISABLE )

if FieldType( nFieldPos ) = "C" //


Solo en caso de cambios de Caracter, igualamos tamaño
xValue := (nArea)->( FieldGet( nFieldPos ) )
xTrigVal := padr( alltrim( xTrigVal ),
FieldSize( nFieldPos ) )
else
xValue := (nArea)->( FieldGet( nFieldPos ) )
endif

if xTrigVal != xValue
xIdValue := cValtoChar( &( cIdValue ) )
if !empty( xIdValue ) // Si hay algún valor, se
guarda, en registros nuevos, el valor esta vacio, no hay que dejar
log
LOG " TABLE:" + ALIAS( nArea ) +;
" FIELD:" + (nArea)-
>( FieldName( nFieldPos ) ) +;
" ID VALUE:" + xIdValue +;
" OLD VALUE:" + alltrim( cValtoChar( (nArea)-
>( FieldGet( nFieldPos ) ) ) ) +;
" NEW VALUE:" +
alltrim( cValtoChar( xTrigVal ) )+;
" DATETIME:" + hb_ttos( hb_datetime() ) +;
" HOSTNAME:" + netname()
endif
endif

sx_SetTrigger( TRIGGER_ENABLE )

CASE nEvent == EVENT_GET

CASE nEvent == EVENT_PRECLOSE

CASE nEvent == EVENT_POSTCLOSE

CASE nEvent == EVENT_PREMEMOPACK

CASE nEvent == EVENT_POSTMEMOPACK


ENDCASE

Return( .T. )

/*
Devuelve el ID a usar segun la tabla.
hLogId es un hash que contiene la tabla y el valor de una
expresion de esa tabla que usaremos
para identificar el registro en el log.
Generalmente, se debe usar un PK, una clave única.
*/
static function __GetIdLog( cTable )
Local cId
static hLogId := { "TEST" => "FIRST + LAST", "TEST2" =>
"SALARY"}

hb_default( @cTable, "" )

cId := HB_HGetDef( hLogId, cTable, "" )

return cId

function cValToChar( u ); return CStr( u )

S-ar putea să vă placă și