El Modelo de Disparadores
Un disparador es una función que PostgreSQL llama automáticamente cuando ocurre un evento DML específico en una tabla, y todo el modelo está construido en torno a responder una pregunta: exactamente cuándo, y cuántas veces, ocurre esa llamada en relación con la escritura a la que está reaccionando.
Entender correctamente ese modelo de temporización es lo que separa un disparador que valida o audita datos silenciosamente de uno que los corrompe silenciosamente, causa interbloqueos bajo carga, o se dispara mucho más a menudo de lo que el autor pretendía.
Esta página construye el modelo mental detrás de la temporización, el disparo por fila versus por sentencia, y la interacción del ejecutor con MVCC para que Conceptos Básicos de Disparadores, Tablas de Transición, Disparadores de Auditoría, y Reglas (Legado) se lean como un sistema coherente en lugar de un montón de variantes de sintaxis.
Resumen
- Un disparador vincula una función a un evento DML con una temporización específica (BEFORE/AFTER/INSTEAD OF) y granularidad (ROW/STATEMENT), y PostgreSQL llama a esa función automáticamente cada vez que el evento coincide.
- Por Qué Importa: La temporización y la granularidad que elijas determinan si el disparador aún puede cambiar los datos, cuántas veces se ejecuta y qué estado de fila tiene realmente permitido ver.
- Conceptos Clave: temporización (BEFORE/AFTER/INSTEAD OF), granularidad (ROW/STATEMENT), NEW/OLD, tabla de transición, orden de disparadores.
- Cuándo Usar: Para hacer cumplir invariantes que una restricción
CHECKno puede expresar, mantener datos derivados o de auditoría automáticamente, o interceptar escrituras en una vista conINSTEAD OF. - Limitaciones / Compensaciones: Cada disparador añade un costo invisible a cada escritura coincidente, y la lógica oculta en un disparador es fácil de olvidar cuando alguien está depurando cambios de datos inesperados meses después.
- Temas Relacionados: Visibilidad de filas en MVCC, aplicación de restricciones, replicación lógica, el sistema de reglas legado.
Fundamentos
Cada disparador se define por dos opciones independientes: su temporización (BEFORE, AFTER, o INSTEAD OF) y su granularidad (FOR EACH ROW o FOR EACH STATEMENT).
Los disparadores BEFORE ROW se ejecutan una vez por fila afectada antes de que ocurra la escritura, y son los únicos que pueden modificar la fila a través de NEW o cancelar la operación por completo devolviendo NULL.
Los disparadores AFTER ROW se ejecutan una vez por fila afectada después de que la escritura ha ocurrido, y pueden ver la fila final pero no pueden cambiar lo que se escribió, solo pueden reaccionar a ello.
Los disparadores INSTEAD OF existen solo en vistas, y reemplazan la escritura por completo, que es cómo una vista normalmente de solo lectura puede aceptar sentencias INSERT, UPDATE, o DELETE.
Los disparadores a nivel de STATEMENT se disparan exactamente una vez por sentencia SQL, independientemente de cuántas filas haya tocado, intercambiando el detalle por fila por una invocación única y barata.
Una analogía útil es un puesto de control de seguridad en una línea de ensamblaje: un disparador BEFORE ROW es un puesto de control que aún puede rechazar o reetiquetar un artículo antes de que salga de la estación, mientras que un disparador AFTER ROW es una cámara más abajo en la línea que solo registra lo que ya se envió.
Mecánica e Interacciones
Cada función de disparador devuelve el pseudotipo especial trigger, y para los disparadores a nivel de fila, el valor de retorno tiene significado: un disparador BEFORE que devuelve NULL cancela la operación para esa fila, mientras que devolver NEW (posiblemente modificado) permite que continúe.
CREATE FUNCTION app.touch_updated() RETURNS trigger
LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$$;Cuando múltiples disparadores comparten la misma tabla y temporización, PostgreSQL los ejecuta en orden alfabético por nombre de disparador, no por orden de creación, que es por lo que los equipos que necesitan una secuencia predecible prefijan los nombres de los disparadores deliberadamente.
Los disparadores AFTER se ejecutan dentro de la misma transacción que la escritura que los disparó, por lo que cualquier fila que lean refleja los propios cambios no confirmados de esa transacción bajo las reglas de visibilidad normales de MVCC, pero que el disparador sea AFTER no significa que la transacción externa se haya confirmado todavía.
Las tablas de transición, declaradas con REFERENCING NEW TABLE AS ... OLD TABLE AS ..., exponen el conjunto completo de filas que un disparador a nivel de sentencia afectó como si fuera una tabla real, brindando acceso masivo a las filas modificadas sin pagar el costo por fila de un disparador a nivel de fila.
A nivel de fila: fila 1 -> disparar fila 2 -> disparar fila 3 -> disparar (N llamadas)
A nivel de sentencia: [fila1, fila2, fila3] -> disparar una vez (1 llamada, ve todas las filas a través de la tabla de transición)Una cláusula WHEN permite que un disparador a nivel de fila evite dispararse por completo cuando una condición sobre OLD/NEW no se cumple, la cual se evalúa antes de que la función del disparador sea invocada y es significativamente más barata que verificar la misma condición como la primera línea dentro del cuerpo de la función.
Consideraciones Avanzadas y Aplicaciones
El sistema de reglas más antiguo (CREATE RULE) funciona reescribiendo la consulta entrante en sí misma en una consulta diferente antes de la ejecución, lo cual es un mecanismo fundamentalmente diferente al de la llamada a función por fila o por sentencia de un disparador, y es por eso que las reglas pueden cambiar silenciosamente el número de filas devueltas por los comandos de maneras que los disparadores nunca lo hacen.
La propia documentación de PostgreSQL dirige la mayor parte del código nuevo hacia disparadores y disparadores INSTEAD OF en vistas en lugar de reglas, ya que las reglas interactúan de manera confusa con comandos multi-sentencia y son mucho más difíciles de razonar; el único lugar donde las reglas todavía importan en la práctica es la reescritura de vistas estilo INSTEAD OF que precede al mecanismo más limpio de disparadores de vistas.
Los disparadores también interactúan con la replicación lógica de una manera que sorprende a la gente: por defecto, un disparador en una tabla del lado del suscriptor no se dispara para las filas aplicadas por la replicación, y debes marcarlo explícitamente ENABLE REPLICA TRIGGER o ENABLE ALWAYS TRIGGER si necesitas que también se ejecute allí.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Disparador BEFORE ROW | Puede validar o transformar datos antes de que se escriban | Añade sobrecarga por fila a cada escritura coincidente | Hacer cumplir invariantes, establecer columnas derivadas como updated_at |
| Disparador AFTER ROW | Ve la fila final confirmada dentro de la transacción | No puede cambiar lo que se escribió | Registro de auditoría, efectos secundarios en cascada |
| Nivel de Sentencia + tabla de transición | Una invocación por sentencia, acceso masivo a filas modificadas | No hay NEW/OLD por fila, debe consultar la tabla de transición | Captura de auditoría masiva, efectos secundarios agregados en escrituras grandes |
| Disparador INSTEAD OF en vista | Hace que una vista compleja sea escribible | Debe reimplementar la lógica de escritura manualmente | Vistas actualizables sobre uniones o columnas calculadas |
| Regla Legada | Reescribe la consulta antes de la ejecución | Interacción confusa con comandos multi-sentencia, difícil de depurar | Casos de reescritura de vistas estrechos y legados solamente |
Un modo de fallo operativo bien conocido es la cadena de disparadores en cascada, donde un disparador en la tabla A actualiza la tabla B, lo que dispara un disparador que actualiza la tabla A nuevamente, y sin una condición de parada deliberada, esa cadena puede entrar en bucle o simplemente hacer que una escritura aparentemente inocua sea mucho más lenta de lo que parece.
Conceptos Erróneos Comunes
- "Los disparadores se ejecutan en su propia transacción separada." Un disparador se ejecuta dentro de la misma transacción exacta que la sentencia que lo disparó, por lo que si esa transacción se revierte, todo lo que hizo el disparador también se revierte.
- "Devolver
NULLde cualquier disparador cancela la escritura." Solo los disparadoresBEFORE ROWpueden cancelar una operación de esta manera, el valor de retorno de un disparadorAFTERse ignora por completo. - "El orden de disparo de los disparadores sigue el orden de creación." PostgreSQL ejecuta disparadores con la misma temporización en orden alfabético por nombre de disparador, que es por lo que los prefijos de nombres deliberados importan para la lógica sensible al orden.
- "Las reglas y los disparadores son básicamente la misma característica con diferente sintaxis." Una regla reescribe la consulta en sí antes de la ejecución, mientras que un disparador llama a una función por fila o por sentencia después de que la consulta ya está planificada, y los dos producen un comportamiento muy diferente en comandos multi-fila.
- "Un disparador a nivel de sentencia no puede ver qué filas cambiaron." Un disparador a nivel de sentencia con una tabla de transición puede ver todas las filas afectadas a la vez, simplemente no puede referenciar
NEW/OLDpara una sola fila como lo hace un disparador a nivel de fila.
Preguntas Frecuentes
¿Cuáles son las dos opciones independientes que definen cada disparador?
Su temporización (BEFORE, AFTER, o INSTEAD OF) y su granularidad (FOR EACH ROW o FOR EACH STATEMENT).
¿Qué tipo de disparador puede realmente cambiar los datos que se están escribiendo?
Solo un disparador BEFORE ROW, ya que se ejecuta antes de que ocurra la escritura y puede modificar NEW o cancelar la operación devolviendo NULL.
¿Puede un disparador AFTER detener la escritura?
No, el valor de retorno de un disparador AFTER se ignora; solo puede observar la fila y reaccionar a ella, por ejemplo, lanzando una excepción para revertir toda la transacción.
¿En qué orden se disparan múltiples disparadores en la misma tabla y temporización?
En orden alfabético por nombre de disparador, no por el orden en que fueron creados, que es por lo que los equipos prefijan los nombres de los disparadores cuando una secuencia específica importa.
¿Cuál es el propósito de una tabla de transición?
- Brinda a un disparador a nivel de sentencia acceso masivo a cada fila que una sentencia afectó
- Evita la sobrecarga por fila de un disparador a nivel de fila en escrituras grandes
- Se referencia con
REFERENCING NEW TABLE AS ... OLD TABLE AS ...
¿Cómo ayuda una cláusula `WHEN` al rendimiento?
Se evalúa antes de que la función del disparador sea invocada, por lo que una fila que no cumple la condición nunca incurre en el costo de entrar en el cuerpo de la función.
¿Los disparadores se disparan cuando la replicación lógica aplica cambios en un suscriptor?
No por defecto, un disparador debe ser marcado explícitamente ENABLE REPLICA TRIGGER o ENABLE ALWAYS TRIGGER para ejecutarse contra filas aplicadas por replicación.
¿Qué es realmente diferente entre una regla y un disparador?
Una regla reescribe la consulta entrante en una consulta diferente antes de la ejecución, mientras que un disparador llama a una función por fila o por sentencia después de que la consulta ya está planificada y en ejecución.
¿Se recomiendan todavía las reglas para código nuevo?
Generalmente no, la propia guía de PostgreSQL favorece los disparadores, incluidos los disparadores INSTEAD OF en vistas, porque las reglas interactúan de manera confusa con comandos multi-sentencia.
¿Qué causa una cadena de disparadores en cascada?
Un disparador en una tabla modifica otra tabla cuyo propio disparador modifica la primera tabla nuevamente, y sin una condición de parada explícita, ese bucle puede degradar el rendimiento o ejecutarse indefinidamente.
¿Puedo deshabilitar temporalmente un disparador para una carga masiva?
Sí, con ALTER TABLE ... DISABLE TRIGGER trigger_name, aunque deberías volver a habilitarlo antes de que la tabla vuelva al tráfico normal de la aplicación y documentar la ventana de mantenimiento.
¿Cuándo debería evitar los disparadores por completo?
Cuando la misma lógica puede vivir limpiamente en el código de la aplicación o en una función de base de datos llamada explícitamente, ya que un disparador oculta la lógica a cualquiera que lea una sentencia INSERT/UPDATE simple.
Relacionados
- Conceptos Básicos de Disparadores - BEFORE/AFTER, fila vs sentencia, práctico
- Tablas de Transición - acceso masivo OLD/NEW a nivel de sentencia
- Disparadores de Auditoría - patrones de captura de cambios sin matar el rendimiento de escritura
- Reglas (Legado) - el mecanismo de reescritura de consultas que los disparadores reemplazaron en su mayoría
- Cuándo No Usar Disparadores - la compensación frente a la lógica a nivel de aplicación
- PL/pgSQL Explicado - el lenguaje en el que se escribe cada función de disparador
Versiones de Stack: Esta página fue escrita para PostgreSQL 18.4 (línea estable 18, línea de mantenimiento 17), donde el modelo de temporización de disparadores, las tablas de transición y el comportamiento de disparo de réplicas de disparadores descritos aquí son actuales.