37.3. Escribir funciones de disparo en C #

Esta sección describe los detalles de bajo nivel de la interfaz para una función de disparo. Esta información sólo se necesita cuando se escriben funciones de disparo en C. Si estás utilizando un lenguaje de nivel superior, estos detalles se manejan por ti. En la mayoría de los casos, deberías considerar el uso de un lenguaje procedimental antes de escribir tus disparadores en C. La documentación de cada lenguaje procedimental explica cómo escribir un disparador en ese lenguaje.

Las funciones de disparo deben utilizar la interfaz del gestor de funciones versión 1.

Cuando una función es llamada por el administrador de disparadores, no se le pasan argumentos normales, sino que se le pasa un puntero de contexto que apunta a una estructura TriggerData. Las funciones en C pueden verificar si fueron llamadas desde el administrador de disparadores o no ejecutando la macro:

CALLED_AS_TRIGGER(fcinfo)

que se expande a:

((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))

Si esto devuelve true, entonces es seguro convertir fcinfo->context al tipo TriggerData * y hacer uso de la estructura TriggerData a la que apunta. La función no debe alterar la estructura TriggerData ni ninguno de los datos a los que apunta.

La estructura struct TriggerData está definida en commands/trigger.h:

typedef struct TriggerData
{
    NodeTag          type;
    TriggerEvent     tg_event;
    Relation         tg_relation;
    HeapTuple        tg_trigtuple;
    HeapTuple        tg_newtuple;
    Trigger         *tg_trigger;
    TupleTableSlot  *tg_trigslot;
    TupleTableSlot  *tg_newslot;
    Tuplestorestate *tg_oldtable;
    Tuplestorestate *tg_newtable;
    const Bitmapset *tg_updatedcols;
} TriggerData;

donde los miembros se definen de la siguiente manera:

type

Siempre T_TriggerData.

tg_event

Describe el evento para el cual se llama a la función. Puedes utilizar las siguientes macros para examinar tg_event:

TRIGGER_FIRED_BEFORE(tg_event)

Devuelve true si el disparador se activó antes de la operación.

TRIGGER_FIRED_AFTER(tg_event)

Devuelve true si el disparador se activó después de la operación.

TRIGGER_FIRED_INSTEAD(tg_event)

Devuelve true si el disparador se activó en lugar de la operación.

TRIGGER_FIRED_FOR_ROW(tg_event)

Devuelve true si el disparador se activó para un evento a nivel de fila.

TRIGGER_FIRED_FOR_STATEMENT(tg_event)

Devuelve true si el disparador se activó para un evento a nivel de sentencia.

TRIGGER_FIRED_BY_INSERT(tg_event)

Devuelve true si el disparador fue activado por un comando INSERT.

TRIGGER_FIRED_BY_UPDATE(tg_event)

Devuelve true si el disparador fue activado por un comando UPDATE.

TRIGGER_FIRED_BY_DELETE(tg_event)

Devuelve true si el disparador fue activado por un comando DELETE.

TRIGGER_FIRED_BY_TRUNCATE(tg_event)

Devuelve true si el disparador fue activado por un comando TRUNCATE.

tg_relation

Un puntero a una estructura que describe la relación para la cual se activó el disparador. Consulta utils/rel.h para obtener detalles sobre esta estructura. Lo más interesante es tg_relation->rd_att (descriptor de las tuplas de la relación) y tg_relation->rd_rel->relname (nombre de la relación; el tipo no es char* sino NameData; utiliza SPI_getrelname(tg_relation) para obtener un char* si necesitas una copia del nombre).

tg_trigtuple

Un puntero a la fila para la cual se activó el disparador. Esta es la fila que se está insertando, actualizando o eliminando. Si este disparador se activó para un INSERT o un DELETE, esto es lo que debes devolver de la función si no deseas reemplazar la fila con una diferente (en el caso de INSERT) o saltarte la operación. Para disparadores en tablas foráneas, los valores de las columnas del sistema aquí no están especificados.

tg_newtuple

Un puntero a la nueva versión de la fila, si el disparador se activó para un UPDATE, y NULL si es para un INSERT o un DELETE. Esto es lo que debes devolver de la función si el evento es un UPDATE y no deseas reemplazar esta fila por una diferente o saltarte la operación. Para disparadores en tablas foráneas, los valores de las columnas del sistema aquí no están especificados.

tg_trigger

Un puntero a una estructura de tipo Trigger, definida en utils/reltrigger.h:

typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    int16       tgtype;
    char        tgenabled;
    bool        tgisinternal;
    bool        tgisclone;
    Oid         tgconstrrelid;
    Oid         tgconstrindid;
    Oid         tgconstraint;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgnattr;
    int16      *tgattr;
    char      **tgargs;
    char       *tgqual;
    char       *tgoldtable;
    char       *tgnewtable;
} Trigger;

donde tgname es el nombre del disparador, tgnargs es el número de argumentos en tgargs, y tgargs es una matriz de punteros a los argumentos especificados en la sentencia CREATE TRIGGER. Los otros miembros son sólo para uso interno.

tg_trigslot

La ranura (slot) que contiene tg_trigtuple, o un puntero NULL si no hay tal tupla.

tg_newslot

La ranura (slot) que contiene tg_newtuple, o un puntero NULL si no hay tal tupla.

tg_oldtable

Un puntero a una estructura de tipo Tuplestorestate que contiene cero o más filas en el formato especificado por tg_relation, o un puntero NULL if there is no OLD TABLE transition relation.

tg_newtable

Un puntero a una estructura de tipo Tuplestorestate que contiene cero o más filas en el formato especificado por tg_relation, o un puntero NULL si no hay una relación de transición NEW TABLE.

tg_updatedcols

Para disparadores UPDATE, un conjunto de mapa de bits (bitmap) que indica las columnas que fueron actualizadas por el comando que activó el disparador. Las funciones de disparo genéricas pueden usar esto para optimizar acciones al no tener que lidiar con columnas que no fueron modificadas.

Como ejemplo, para determinar si una columna con número de atributo attnum (basado en 1) es miembro de este conjunto de mapa de bits, llama a bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols)).

Para disparadores que no sean de UPDATE, esto será NULL.

Para permitir que las consultas emitidas a través de SPI hagan referencia a las tablas de transición, consulta SPI_register_trigger_data.

Una función de disparo debe devolver un puntero HeapTuple o un puntero NULL (no un valor nulo de SQL, es decir, no establezcas isNull en true). Asegúrate de devolver tg_trigtuple o tg_newtuple, según corresponda, si no deseas modificar la fila sobre la que se está operando.