Aquí tienes un ejemplo muy simple de una función de disparo escrita en C. (Se pueden encontrar ejemplos de disparadores escritos en lenguajes procedimentales en la documentación de los mismos).
La función trigf informa el número de filas en la
tabla ttest y omite la operación real si el
comando intenta insertar un valor nulo en la columna
x. (De modo que el disparador actúa como una restricción de no nulo pero
no aborta la transacción).
Primero, la definición de la tabla:
CREATE TABLE ttest (
x integer
);
Este es el código fuente de la función del disparador:
#include "postgres.h"
#include "fmgr.h"
#include "executor/spi.h" /* esto es lo que necesitas para trabajar con SPI */
#include "commands/trigger.h" /* ... disparadores ... */
#include "utils/rel.h" /* ... y relaciones */
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(trigf);
Datum
trigf(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
TupleDesc tupdesc;
HeapTuple rettuple;
char *when;
bool checknull = false;
bool isnull;
int ret, i;
/* asegurarse de que sea llamado como un disparador */
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "trigf: no llamado por el administrador de disparadores");
/* tupla a devolver al ejecutor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
rettuple = trigdata->tg_newtuple;
else
rettuple = trigdata->tg_trigtuple;
/* comprobar si hay valores nulos */
if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
&& TRIGGER_FIRED_BEFORE(trigdata->tg_event))
checknull = true;
if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
when = "before";
else
when = "after ";
tupdesc = trigdata->tg_relation->rd_att;
/* conectarse al gestor SPI */
SPI_connect();
/* obtener el número de filas en la tabla */
ret = SPI_exec("SELECT count(*) FROM ttest", 0);
if (ret < 0)
elog(ERROR, "trigf (fired %s): SPI_exec devolvió %d", when, ret);
/* count(*) devuelve int8, así que ten cuidado de convertirlo */
i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
SPI_tuptable->tupdesc,
1,
&isnull));
elog (INFO, "trigf (fired %s): hay %d filas en ttest", when, i);
SPI_finish();
if (checknull)
{
SPI_getbinval(rettuple, tupdesc, 1, &isnull);
if (isnull)
rettuple = NULL;
}
return PointerGetDatum(rettuple);
}
Después de compilar el código fuente (consulta Section 36.10.5), declara la función y los disparadores:
CREATE FUNCTION trigf() RETURNS trigger
AS 'filename'
LANGUAGE C;
CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE FUNCTION trigf();
CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
FOR EACH ROW EXECUTE FUNCTION trigf();
Ahora puedes probar el funcionamiento del disparador:
=> INSERT INTO ttest VALUES (NULL);
INFO: trigf (fired before): hay 0 filas en ttest
INSERT 0 0
-- Inserción omitida y el disparador AFTER no se activa
=> SELECT * FROM ttest;
x
---
(0 rows)
=> INSERT INTO ttest VALUES (1);
INFO: trigf (fired before): hay 0 filas en ttest
INFO: trigf (fired after ): hay 1 filas en ttest
^^^^^^^^
recuerda lo que dijimos sobre la visibilidad.
INSERT 167793 1
vac=> SELECT * FROM ttest;
x
---
1
(1 row)
=> INSERT INTO ttest SELECT x * 2 FROM ttest;
INFO: trigf (fired before): hay 1 filas en ttest
INFO: trigf (fired after ): hay 2 filas en ttest
^^^^^^
recuerda lo que dijimos sobre la visibilidad.
INSERT 167794 1
=> SELECT * FROM ttest;
x
---
1
2
(2 rows)
=> UPDATE ttest SET x = NULL WHERE x = 2;
INFO: trigf (fired before): hay 2 filas en ttest
UPDATE 0
=> UPDATE ttest SET x = 4 WHERE x = 2;
INFO: trigf (fired before): hay 2 filas en ttest
INFO: trigf (fired after ): hay 2 filas en ttest
UPDATE 1
vac=> SELECT * FROM ttest;
x
---
1
4
(2 rows)
=> DELETE FROM ttest;
INFO: trigf (fired before): hay 2 filas en ttest
INFO: trigf (fired before): hay 1 filas en ttest
INFO: trigf (fired after ): hay 0 filas en ttest
INFO: trigf (fired after ): hay 0 filas en ttest
^^^^^^
recuerda lo que dijimos sobre la visibilidad.
DELETE 2
=> SELECT * FROM ttest;
x
---
(0 rows)
Hay ejemplos más complejos en src/test/regress/regress.c
y en spi.