O Modelo de Gatilho
Um gatilho é uma função que o PostgreSQL chama automaticamente quando um evento DML específico acontece em uma tabela, e todo o modelo é construído em torno de responder a uma pergunta: exatamente quando, e quantas vezes, essa chamada acontece em relação à escrita da qual está reagindo.
Acertar esse modelo de tempo é o que separa um gatilho que valida ou audita dados silenciosamente de um que os corrompe silenciosamente, causa deadlocks sob carga, ou dispara muito mais vezes do que o autor pretendia.
Esta página constrói o modelo mental por trás do tempo de disparo, disparo por linha versus por instrução, e a interação do executor com MVCC para que Noções Básicas de Gatilhos, Tabelas de Transição, Gatilhos de Auditoria e Regras (Legado) sejam lidos como um sistema coerente em vez de um amontoado de variantes de sintaxe.
Resumo
- Um gatilho vincula uma função a um evento DML com um tempo específico (BEFORE/AFTER/INSTEAD OF) e granularidade (ROW/STATEMENT), e o PostgreSQL chama essa função automaticamente sempre que o evento corresponde.
- Por Que Importa: O tempo e a granularidade que você escolhe determinam se o gatilho ainda pode alterar os dados, quantas vezes ele é executado e qual estado da linha ele realmente pode ver.
- Conceitos Chave: tempo (BEFORE/AFTER/INSTEAD OF), granularidade (ROW/STATEMENT), NEW/OLD, tabela de transição, ordem de gatilhos.
- Quando Usar: Para impor invariantes que uma restrição
CHECKnão consegue expressar, manter dados derivados ou de auditoria automaticamente, ou interceptar escritas em uma view comINSTEAD OF. - Limitações / Trade-offs: Cada gatilho adiciona um custo invisível a cada escrita correspondente, e a lógica oculta em um gatilho é fácil de esquecer quando alguém está depurando alterações inesperadas de dados meses depois.
- Tópicos Relacionados: Visibilidade de linha MVCC, aplicação de restrições, replicação lógica, o sistema de regras legado.
Fundamentos
Todo gatilho é definido por duas escolhas independentes: seu tempo (BEFORE, AFTER, ou INSTEAD OF) e sua granularidade (FOR EACH ROW ou FOR EACH STATEMENT).
Gatilhos BEFORE ROW executam uma vez por linha afetada antes que a escrita ocorra, e são os únicos que podem modificar a linha através de NEW ou cancelar a operação inteiramente retornando NULL.
Gatilhos AFTER ROW executam uma vez por linha afetada após a escrita ter ocorrido, e eles podem olhar para a linha final, mas não podem alterar o que foi escrito, apenas reagir a isso.
Gatilhos INSTEAD OF existem apenas em views, e eles substituem a escrita inteiramente, que é como uma view normalmente somente leitura pode aceitar instruções INSERT, UPDATE ou DELETE.
Gatilhos de nível STATEMENT disparam exatamente uma vez por instrução SQL, independentemente de quantas linhas ela tocou, trocando detalhes por linha por uma invocação única e barata.
Uma analogia útil é um posto de segurança em uma linha de montagem: um gatilho BEFORE ROW é um posto de segurança que ainda pode rejeitar ou renomear um item antes que ele saia da estação, enquanto um gatilho AFTER ROW é uma câmera mais adiante na linha que apenas registra o que já foi enviado.
Mecânicas e Interações
Toda função de gatilho retorna o pseudo-tipo especial trigger, e para gatilhos de nível de linha, o valor de retorno tem significado: um gatilho BEFORE retornando NULL cancela a operação para essa linha, enquanto retornar NEW (possivelmente modificado) permite que ela prossiga.
CREATE FUNCTION app.touch_updated() RETURNS trigger
LANGUAGE plpgsql AS $$
BEGIN
NEW.updated_at := now();
RETURN NEW;
END;
$$;Quando múltiplos gatilhos compartilham a mesma tabela e tempo, o PostgreSQL os dispara em ordem alfabética pelo nome do gatilho, não pela ordem de criação, que é por isso que equipes que precisam de uma sequência previsível prefixam os nomes dos gatilhos deliberadamente.
Gatilhos AFTER executam dentro da mesma transação que a instrução que os disparou, então qualquer linha que eles leiam reflete as próprias alterações não confirmadas daquela transação sob as regras normais de visibilidade MVCC, mas o fato de o gatilho ser AFTER não significa que a transação externa já foi confirmada.
Tabelas de transição, declaradas com REFERENCING NEW TABLE AS ... OLD TABLE AS ..., expõem o conjunto completo de linhas que um gatilho de nível de instrução afetou como se fosse uma tabela real, dando acesso em lote a linhas alteradas sem pagar o custo por linha de um gatilho de nível de linha.
Nível de linha: linha 1 -> dispara linha 2 -> dispara linha 3 -> dispara (N chamadas)
Nível de instrução: [linha1, linha2, linha3] -> dispara uma vez (1 chamada, vê todas as linhas via tabela de transição)Uma cláusula WHEN permite que um gatilho de nível de linha pule o disparo inteiramente quando uma condição em OLD/NEW não é atendida, que é avaliada antes mesmo que a função do gatilho seja invocada e é significativamente mais barata do que verificar a mesma condição como a primeira linha dentro do corpo da função.
Considerações Avançadas e Aplicações
O sistema de regras mais antigo (CREATE RULE) funciona reescrevendo a própria consulta de entrada em uma consulta diferente antes da execução, o que é um mecanismo fundamentalmente diferente da chamada de função por linha ou por instrução de um gatilho, e é por isso que as regras podem alterar silenciosamente as contagens de linhas retornadas por comandos de maneiras que os gatilhos nunca fazem.
A própria documentação do PostgreSQL direciona a maior parte do novo código para gatilhos e gatilhos INSTEAD OF em views em vez de regras, já que as regras interagem de forma confusa com comandos multi-instrução e são muito mais difíceis de raciocinar; o único lugar onde as regras ainda importam na prática é a reescrita de views no estilo INSTEAD OF que antecede o mecanismo de gatilho de view mais limpo.
Gatilhos também interagem com a replicação lógica de uma forma que surpreende as pessoas: por padrão, um gatilho em uma tabela do lado do assinante não dispara para linhas aplicadas pela replicação, e você precisa marcá-lo explicitamente como ENABLE REPLICA TRIGGER ou ENABLE ALWAYS TRIGGER se precisar que ele também seja executado lá.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Gatilho BEFORE ROW | Pode validar ou transformar dados antes de serem gravados | Adiciona sobrecarga por linha a cada escrita correspondente | Impor invariantes, definir colunas derivadas como updated_at |
| Gatilho AFTER ROW | Vê a linha final confirmada dentro da transação | Não pode alterar o que foi escrito | Registro de auditoria, efeitos colaterais em cascata |
| Nível de instrução + tabela de transição | Uma invocação por instrução, acesso em lote a linhas alteradas | Sem NEW/OLD por linha, deve consultar a tabela de transição | Captura de auditoria em lote, efeitos colaterais agregados em escritas grandes |
| Gatilho INSTEAD OF em view | Torna uma view complexa gravável | Deve reimplementar a lógica de escrita manualmente | Views graváveis sobre junções ou colunas computadas |
| Regra Legada | Reescreve a consulta antes da execução | Interação confusa com comandos multi-instrução, difícil de depurar | Apenas casos restritos e legados de reescrita de view |
Um modo de falha operacional bem conhecido é a cadeia de gatilhos em cascata, onde um gatilho na tabela A atualiza a tabela B, que dispara um gatilho que atualiza a tabela A novamente, e sem uma condição de parada deliberada, essa cadeia pode entrar em loop ou simplesmente tornar uma escrita aparentemente inofensiva muito mais lenta do que parece.
Equívocos Comuns
- "Gatilhos rodam em sua própria transação separada." Um gatilho executa dentro da mesma transação que a instrução que o disparou, então se essa transação reverter, tudo o que o gatilho fez também reverterá.
- "Retornar
NULLde qualquer gatilho cancela a escrita." Apenas gatilhosBEFORE ROWpodem cancelar uma operação dessa forma, o valor de retorno de um gatilhoAFTERé completamente ignorado. - "A ordem de disparo dos gatilhos segue a ordem de criação." O PostgreSQL dispara gatilhos com o mesmo tempo em ordem alfabética pelo nome do gatilho, que é por isso que prefixos de nomenclatura deliberados importam para lógica sensível à ordem.
- "Regras e gatilhos são basicamente o mesmo recurso com sintaxe diferente." Uma regra reescreve a própria consulta antes da execução, enquanto um gatilho chama uma função por linha ou por instrução depois que a consulta já foi planejada, e os dois produzem comportamentos muito diferentes em comandos multi-linha.
- "Um gatilho de nível de instrução não pode ver quais linhas foram alteradas." Um gatilho de nível de instrução com uma tabela de transição pode ver todas as linhas afetadas de uma vez, apenas não pode referenciar
NEW/OLDpara uma única linha como um gatilho de nível de linha pode.
FAQs
Quais são as duas escolhas independentes que definem todo gatilho?
Seu tempo (BEFORE, AFTER, ou INSTEAD OF) e sua granularidade (FOR EACH ROW ou FOR EACH STATEMENT).
Qual tipo de gatilho pode realmente alterar os dados que estão sendo gravados?
Apenas um gatilho BEFORE ROW, pois ele executa antes que a escrita ocorra e pode modificar NEW ou cancelar a operação retornando NULL.
Um gatilho AFTER pode impedir que a escrita ocorra?
Não, o valor de retorno de um gatilho AFTER é ignorado, ele só pode observar a linha e reagir a ela, por exemplo, levantando uma exceção para reverter toda a transação.
Em que ordem múltiplos gatilhos na mesma tabela e tempo disparam?
Em ordem alfabética pelo nome do gatilho, não pela ordem em que foram criados, que é por isso que as equipes prefixam os nomes dos gatilhos quando uma sequência específica é importante.
Qual o propósito de uma tabela de transição?
- Dá a um gatilho de nível de instrução acesso em lote a cada linha que uma instrução afetou
- Evita a sobrecarga por linha de um gatilho de nível de linha em escritas grandes
- É referenciada com
REFERENCING NEW TABLE AS ... OLD TABLE AS ...
Como uma cláusula `WHEN` ajuda no desempenho?
Ela é avaliada antes mesmo que a função do gatilho seja invocada, então uma linha que não atende à condição nunca paga o custo de entrar no corpo da função.
Gatilhos disparam quando a replicação lógica aplica alterações em um assinante?
Não por padrão, um gatilho precisa ser explicitamente marcado como ENABLE REPLICA TRIGGER ou ENABLE ALWAYS TRIGGER para ser executado contra linhas aplicadas pela replicação.
O que é realmente diferente entre uma regra e um gatilho?
Uma regra reescreve a consulta de entrada em uma consulta diferente antes da execução, enquanto um gatilho chama uma função por linha ou por instrução depois que a consulta já foi planejada e está em execução.
Regras ainda são recomendadas para código novo?
Geralmente não, a própria orientação do PostgreSQL favorece gatilhos, incluindo gatilhos INSTEAD OF em views, porque regras interagem de forma confusa com comandos multi-instrução.
O que causa uma cadeia de gatilhos em cascata?
Um gatilho em uma tabela modifica outra tabela cujo próprio gatilho modifica a primeira tabela novamente, e sem uma condição de parada explícita, esse loop pode degradar o desempenho ou rodar indefinidamente.
Posso desabilitar temporariamente um gatilho para uma carga em lote?
Sim, com ALTER TABLE ... DISABLE TRIGGER nome_do_gatilho, embora você deva reabilitá-lo antes que a tabela retorne ao tráfego normal da aplicação e documentar a janela de manutenção.
Quando devo evitar gatilhos completamente?
Quando a mesma lógica pode viver de forma limpa no código da aplicação ou em uma função de banco de dados chamada explicitamente, já que um gatilho esconde a lógica de qualquer pessoa que esteja lendo uma instrução INSERT/UPDATE simples.
Relacionados
- Noções Básicas de Gatilhos - BEFORE/AFTER, linha vs instrução, na prática
- Tabelas de Transição - acesso em lote OLD/NEW em nível de instrução
- Gatilhos de Auditoria - padrões de captura de alterações sem matar a taxa de transferência de escrita
- Regras (Legado) - o mecanismo de reescrita de consulta que os gatilhos substituíram em grande parte
- Quando Não Usar Gatilhos - o trade-off em relação à lógica em nível de aplicação
- PL/pgSQL Explicado - a linguagem em que cada função de gatilho é escrita
Versões da Pilha: Esta página foi escrita para PostgreSQL 18.4 (linha estável 18, linha de manutenção 17), onde o modelo de tempo de disparo de gatilhos, tabelas de transição e o comportamento de disparo de gatilhos de réplica descritos aqui são atuais.