Hay ejemplos m�s complejos en src/test/regress/regress.c y en contrig/spi.
He aqu� un ejemplo muy sencillo sobre el uso de triggers. La funci�n trigf devuelve el n�mero de tuplas en la relaci�n ttest e ignora la operaci�n si la consulta intenta insertar NULL en x (i.e - act�a como una restricci�n NOT NULL pero no aborta la transacci�n).
#include "executor/spi.h" /* Necesario para trabajar con SPI */ #include "commands/trigger.h" /* -"- y triggers */ HeapTuple trigf(void); HeapTuple trigf() { TupleDesc tupdesc; HeapTuple rettuple; char *when; bool checknull = false; bool isnull; int ret, i; if (!CurrentTriggerData) elog(WARN, "trigf: triggers sin inicializar"); /* tupla para devolver al Ejecutor */ if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event)) rettuple = CurrentTriggerData->tg_newtuple; else rettuple = CurrentTriggerData->tg_trigtuple; /* comprobar NULLs ? */ if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) && TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) checknull = true; if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) when = "antes "; else when = "despu�s "; tupdesc = CurrentTriggerData->tg_relation->rd_att; CurrentTriggerData = NULL; /* Conexi�n al gestor SPI */ if ((ret = SPI_connect()) < 0) elog(WARN, "trigf (lanzado %s): SPI_connect devolvi� %d", when, ret); /* Obtiene el n�mero de tuplas en la relaci�n */ ret = SPI_exec("select count(*) from ttest", 0); if (ret < 0) elog(WARN, "trigf (lanzado %s): SPI_exec devolvi� %d", when, ret); i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull); elog (NOTICE, "trigf (lanzado %s): hay %d tuplas en ttest", when, i); SPI_finish(); if (checknull) { i = SPI_getbinval(rettuple, tupdesc, 1, &isnull); if (isnull) rettuple = NULL; } return (rettuple); } |
Ahora, compila y create table ttest (x int4); create function trigf () returns opaque as '...path_to_so' language 'c';
vac=> create trigger tbefore before insert or update or delete on ttest for each row execute procedure trigf(); CREATE vac=> create trigger tafter after insert or update or delete on ttest for each row execute procedure trigf(); CREATE vac=> insert into ttest values (null); NOTICE:trigf (fired before): there are 0 tuples in ttest INSERT 0 0 -- Insertion skipped and AFTER trigger is not fired vac=> select * from ttest; x - (0 rows) vac=> insert into ttest values (1); NOTICE:trigf (fired before): there are 0 tuples in ttest NOTICE:trigf (fired after ): there are 1 tuples in ttest ^^^^^^^^ remember what we said about visibility. INSERT 167793 1 vac=> select * from ttest; x - 1 (1 row) vac=> insert into ttest select x * 2 from ttest; NOTICE:trigf (fired before): there are 1 tuples in ttest NOTICE:trigf (fired after ): there are 2 tuples in ttest ^^^^^^^^ remember what we said about visibility. INSERT 167794 1 vac=> select * from ttest; x - 1 2 (2 rows) vac=> update ttest set x = null where x = 2; NOTICE:trigf (fired before): there are 2 tuples in ttest UPDATE 0 vac=> update ttest set x = 4 where x = 2; NOTICE:trigf (fired before): there are 2 tuples in ttest NOTICE:trigf (fired after ): there are 2 tuples in ttest UPDATE 1 vac=> select * from ttest; x - 1 4 (2 rows) vac=> delete from ttest; NOTICE:trigf (fired before): there are 2 tuples in ttest NOTICE:trigf (fired after ): there are 1 tuples in ttest NOTICE:trigf (fired before): there are 1 tuples in ttest NOTICE:trigf (fired after ): there are 0 tuples in ttest ^^^^^^^^ remember what we said about visibility. DELETE 2 vac=> select * from ttest; x - (0 rows) |
Aportaci�n del traductor.Manuel Mart�nez Valls En la version 6.4 ya existian los triggers, lo que eran triggers para tuplos, (FOR EACH ROW) pero no para sentencias (FOR STATEMENT), por eso creo que es importante poner disparadores para sentencias, no disparadores solo. Los trigger son parte de lo que se conoce como "elementos activos" de una BD. Asi como lo son las constraints tales como NOT NULL, FOREIGN KEY, PRIMARY KEY, CHECK. Una vez definidas ellas "se activaran" solo al ocurrir un evento que las viole, un valor nulo en un campo con NOT NULL, etc �Por que entonces llamar triggers a los triggers? ;Con ellos se quizo dar mas control al programador sobre los eventos que desencadenan un elemento activo, se le conoce en ingles como ECA rules o event-condition-action rule. Es por ello que los triggers tienen una clausula BEFORE, AFTER o INSTEAD (por cierto pgsql no tiene INSTEAD) y bajo que evento (INSERT, UPDATE, DELETE) pero de esta forma el trigger se ejecutara para tuplo (o fila) sometido al evento (clausula FOR EACH ROW) pero el standard (que pgsql no cubre completamente) dice que puede ser tambien FOR EACH SENTENCE. Esto provoca que se ejecute el trigger para toda la relacion (o tabla) para la cual se define (clausula ON). La diferencia para los que lo han programado, por ejemplo en plpgsql, queda clara entonces: cuando es FOR EACH ROW en la funcion pgsql que implementa el trigger se tiene un objeto NEW y uno OLD que se refiere a la tupla completa, en el trigger de STATEMENT tiene un objeto NEW y OLD que son la relacion (o tabla) completa Esta claro entonces que es un poco mas dificil implementar un trigger para statement que para fila (todavia pgsql no lo tiene). Finalmente este es un buen ejemplo de que por que pgsql dice que "implementa un subconjunto extendido de SQL92", no hay trigger en SQL92, son del SQL3. |