Um gatilho especifica que o banco de dados deve executar automaticamente uma determinada função sempre que um determinado tipo de operação for executado. Os gatilhos podem estar ligados a tabelas (particionadas ou não), visões e tabelas estrangeiras
Nas tabelas e tabelas estrangeiras, os gatilhos podem ser definidos
de modo a serem executados antes ou após qualquer operação de
INSERT, UPDATE ou
DELETE, uma vez por linha modificada,
ou uma vez por-instrução SQL.
Além disso, os gatilhos de UPDATE podem ser
configurados para disparar apenas se certas colunas
mencionadas na cláusula SET da instrução
UPDATE forem modificadas.
Os gatilhos também podem disparar para instruções
TRUNCATE.
Se ocorrer um evento de gatilho, a função de gatilho será chamada
no momento apropriado para lidar com o evento.
Nas visões, os gatilhos podem ser definidos para executar em vez
das operações INSERT, UPDATE,
ou DELETE.
Estes gatilhos INSTEAD OF são disparados uma vez
para cada linha na visão que precisa ser modificada.
É responsabilidade da função do gatilho realizar as alterações
necessárias na(s) tabela(s) base subjacente(s) da visão e,
quando apropriado, retornar a linha modificada conforme
aparecerá na visão.
Os gatilhos em visões também podem ser definidos para serem
executados uma vez por-instrução SQL,
e antes ou depois das operações INSERT,
UPDATE ou DELETE.
Entretanto, estes gatilhos são disparados apenas se houver também
um gatilho INSTEAD OF na visão.
Caso contrário, qualquer instrução direcionada à visão deve ser
reescrita em uma instrução que afete sua(s) tabela(s) base
subjacente(s) e, em seguida, os gatilhos que serão disparados
são aqueles anexados à(s) tabela(s) base.
A função de gatilho deve ser definida antes que o próprio gatilho
possa ser criado.
A função de gatilho deve ser declarada como uma função sem argumentos,
e retornando o tipo trigger.
(A função de gatilho recebe sua entrada por meio da estrutura
TriggerData passada especialmente,
e não na forma de argumentos de função comuns.)
Uma vez criada a função de gatilho adequada, o gatilho é estabelecido com o comando CREATE TRIGGER. A mesma função de gatilho pode ser usada para vários gatilhos.
O PostgreSQL oferece os gatilhos
por-linha e por-instrução.
Com um gatilho por-linha, a função de gatilho é chamada uma vez
para cada linha afetada pela instrução que disparou o gatilho.
Por outro lado, um gatilho por-instrução é chamado apenas uma vez
quando uma instrução apropriada é executada, independentemente do
número de linhas afetadas por esta instrução.
Em particular, uma instrução que afeta zero linhas ainda resultará
na execução de quaisquer gatilhos por-instrução aplicáveis.
Estes dois tipos de gatilhos às vezes são chamados de gatilhos no
nível de linha, e gatilhos no
nível de instrução, respectivamente.
Os gatilhos para TRUNCATE só podem ser definidos
no nível de instrução, e não por linha.
Os gatilhos também são classificados conforme o disparo aconteça
antes, após, ou
em vez da operação,
sendo referidos como gatilhos BEFORE,
gatilhos AFTER e gatilhos
INSTEAD OF, respectivamente.
Os gatilhos BEFORE no nível de instrução disparam
naturalmente antes que a instrução comece a fazer qualquer coisa,
enquanto os gatilhos AFTER no nível da instrução
disparam bem no final da instrução.
Estes tipos de gatilhos podem ser definidos em tabelas, visões,
ou tabelas estrangeiras.
Gatilhos BEFORE no nível de linha disparam
imediatamente antes de uma determinada linha ser operada,
enquanto gatilhos AFTER no nível de linha
disparam no final da instrução (mas antes de qualquer gatilho no
nível de instrução AFTER).
Estes tipos de gatilhos só podem ser definidos em tabelas e
tabelas estrangeiras, e não em visões.
Os gatilhos INSTEAD OF só podem ser definidos
em visões, e apenas no nível de linha; eles disparam imediatamente
quando cada linha na visão é identificada como precisando ser operada.
A execução de um gatilho AFTER pode ser adiada
para o final da transação, em vez do final da instrução, se for
definido como um gatilho de restrição.
Em todos os casos, o gatilho é executado como parte da mesma
transação que a instrução que o disparou, portanto, se a instrução
ou o gatilho causar um erro, os efeitos dos dois serão desfeitos.
Além disso, o gatilho sempre será executado como a função de banco
de dados (role) que enfileirou o
evento do gatilho, a menos que a função do gatilho esteja marcada
como SECURITY DEFINER, caso em que será
executada como o dono da função.
Se o comando INSERT tiver uma cláusula
ON CONFLICT DO UPDATE,
será possível que gatilhos no nível de linha
BEFORE INSERT e, em seguida,
BEFORE UPDATE
sejam executados nas linhas envolvidas.
Estas interações podem ser complexas se os gatilhos não forem
idempotentes [119]), porque as alterações feitas pelos gatilhos
BEFORE INSERT serão vistas
pelos gatilhos BEFORE UPDATE,
incluindo alterações em colunas EXCLUDED.
Note que os gatilhos UPDATE no nível de instrução
são executados quando ON CONFLICT DO UPDATE é
especificado, independentemente de quaisquer linhas terem sido
afetadas ou não pelo UPDATE
(e independentemente de ter sido seguido um caminho alternativo de
UPDATE).
Um comando INSERT com a cláusula
ON CONFLICT DO UPDATE executará primeiro os
gatilhos BEFORE INSERT no
nível de instrução, em seguida, os gatilhos
BEFORE UPDATE
no nível de instrução, seguido pelos gatilhos
AFTER UPDATE
no nível de instrução e, finalmente, os gatilhos
AFTER INSERT
no nível de instrução.
Uma instrução que tem como alvo uma tabela mãe em uma hierarquia de herança ou particionamento não faz com que os gatilhos em nível de instrução das tabelas filhas afetadas sejam acionados; somente os gatilhos no nível de instrução da tabela mãe são disparados. Entretanto, os gatilhos no nível de linha de quaisquer tabelas filhas afetadas serão disparados.
Se um UPDATE em uma tabela particionada fizer com
que a linha seja movida para outra partição, isto será executado
como um DELETE na partição original, seguido por
um INSERT na nova partição.
Neste caso, todos os gatilhos BEFORE
UPDATE no nível de linha, e todos os gatilhos
BEFORE DELETE no nível de
linha, são disparados na partição original.
Em seguida, todos os gatilhos BEFORE
INSERT no nível de linha serão disparados na
partição de destino.
A possibilidade de resultados surpreendentes deve ser considerada
quando todos estes gatilhos afetam a linha que está sendo movida.
No que diz respeito aos gatilhos AFTER ROW,
os gatilhos AFTER DELETE e
AFTER INSERT são aplicados;
mas os gatilhos AFTER UPDATE
não são aplicados, porque o UPDATE foi convertido
em um DELETE e um INSERT.
No que diz respeito aos gatilhos no nível de instrução, nenhum dos
gatilhos de DELETE ou INSERT
é disparado, mesmo que ocorra movimentação de linha; somente os
gatilhos para UPDATE definidos na tabela de
destino usada na instrução UPDATE serão disparados.
Não são definidos gatilhos em separado para MERGE.
Em vez disso, os gatilhos para UPDATE,
DELETE e INSERT,
no nível de instrução ou de linha, são disparados dependendo
(para os gatilhos no nível de instrução) das ações especificadas
na consulta MERGE e
(para os gatilhos no nível de linha) das ações executadas.
Ao executar um comando MERGE, os gatilhos
BEFORE e AFTER no nível de
instrução são disparados para eventos especificados nas ações do
comando MERGE, independentemente da ação ser
ou não ser executada.
É semelhante a uma instrução UPDATE que não
atualiza nenhuma linha, mas mesmo assim os gatilhos no nível de
instrução são disparados
Os gatilhos no nível de linha são disparados somente quando uma
linha é efetivamente atualizada, inserida ou excluída.
Portanto, é perfeitamente legal que, embora gatilhos no nível de
instrução sejam disparados para certos tipos de ação, nenhum
gatilho no nível de linha seja disparado para o mesmo tipo de ação.
As funções de gatilho chamadas por gatilhos por-instrução devem
sempre retornar NULL.
As funções de gatilho chamadas por gatilhos por-linha podem
retornar uma linha de tabela
(um valor do tipo de dados HeapTuple)
para o executor da chamada, se desejarem.
Um gatilho no nível de linha disparado antes de uma operação tem as
seguintes opções:
Pode retornar NULL para saltar a operação para
a linha corrente.
Isto instrui o executor a não executar a operação no
nível de linha que chamou o gatilho (a inserção, alteração
ou exclusão de uma determinada linha da tabela).
Somente em gatilhos para INSERT e
UPDATE no nível de linha, a linha retornada
torna-se a linha que será inserida ou irá substituir a linha
que está sendo atualizada.
Isto permite que a função de gatilho modifique a linha que está
sendo inserida ou atualizada.
Um gatilho BEFORE no nível de linha, que não
pretenda causar nenhum desses comportamentos, deve ter o cuidado
de retornar como resultado a mesma linha que foi passada
(ou seja, a linha NEW nos gatilhos para
INSERT e UPDATE, ou a linha
OLD nos gatilhos para DELETE).
Um gatilho INSTEAD OF no nível de linha deve
retornar NULL para indicar que não modificou
nenhum dado das tabelas-base subjacentes da visão, ou deve retornar
a linha da visão que foi passada
(a linha NEW para operações de
INSERT e UPDATE, ou a linha
OLD para as operações de DELETE).
Um valor não nulo retornado é usado para sinalizar que o gatilho
executou as alterações de dados necessárias na visão.
Isto fará com que a contagem do número de linhas afetadas pelo
comando seja incrementada.
Somente para operações de INSERT e
UPDATE, o gatilho pode modificar a linha
NEW antes de retorná-la.
Isto muda os dados retornados por INSERT RETURNING
ou UPDATE RETURNING, sendo útil quando a visão
não mostra os mesmos dados fornecidos.
O valor retornado é ignorado para gatilhos no nível de linha disparados
após uma operação, portanto, podem retornar NULL.
Algumas considerações se aplicam às colunas geradas.
As colunas geradas armazenadas são calculadas após os gatilhos
BEFORE, e antes dos gatilhos AFTER.
Portanto, o valor gerado pode ser inspecionado nos gatilhos
AFTER.
Nos gatilhos BEFORE, a linha OLD
contém o antigo valor gerado, como seria de esperar, mas a linha
NEW ainda não contém o novo valor gerado,
não devendo ser acessado.
Na interface da linguagem C, o conteúdo da coluna
é indefinido neste ponto; uma linguagem de programação de nível mais
alto, deve impedir o acesso a uma coluna gerada armazenada na linha
NEW em um gatilho BEFORE.
Mudanças no valor de uma coluna gerada em um gatilho
BEFORE são ignoradas e serão sobrescritas.
As colunas virtuais geradas nunca são calculadas quando os gatilhos
são disparados.
Na interface da linguagem C, seu conteúdo é
indefinido em uma função de gatilho.
Linguagens de programação de alto nível devem impedir o acesso a
colunas virtuais geradas em gatilhos.
Se for definido mais de um gatilho para o mesmo evento na mesma
relação, os gatilhos serão disparados em ordem alfabética pelo
nome do gatilho.
No caso dos gatilhos BEFORE e
INSTEAD OF, a linha possivelmente modificada
retornada por cada gatilho torna-se a entrada para o próximo gatilho.
Se qualquer gatilho BEFORE ou
INSTEAD OF retornar NULL,
a operação será abandonada para esta linha, e os gatilhos
subsequentes não serão disparados (para esta linha).
Uma definição de gatilho também pode especificar uma condição
booleana WHEN, que será testada para ver se
o gatilho deve ser disparado.
Em gatilhos no nível de linha, a condição WHEN
pode examinar os valores antigos e/ou novos das colunas da linha.
(Gatilhos no nível de instrução também podem ter condições
WHEN, embora o recurso não seja tão útil para eles.)
Em um gatilho BEFORE, a condição
WHEN é avaliada logo antes da função ser
executada, então usar WHEN não é materialmente
diferente de testar a mesma condição no início da função de gatilho.
Entretanto, em um gatilho AFTER, a condição
WHEN é avaliada logo após a ocorrência da
atualização da linha, e determina se um evento é colocado na fila
para disparar o gatilho no final da instrução.
Portanto, quando a condição WHEN de um gatilho
AFTER não retorna verdade, não é necessário
colocar um evento na fila, nem buscar novamente a linha no final
da instrução.
Isto pode resultar em acelerações significativas em instruções que
modificam muitas linhas, se o gatilho precisar ser disparado apenas
para algumas das linhas.
Os gatilhos INSTEAD OF não dão suporte a
condições WHEN.
Normalmente, os gatilhos BEFORE no nível de linha
são usados para verificar ou modificar os dados que serão inseridos
ou atualizados.
Por exemplo, um gatilho BEFORE pode ser usado
para inserir a hora corrente em uma coluna timestamp,
ou para verificar se dois elementos da linha são consistentes.
Os gatilhos AFTER no nível de linha são usados
de maneira mais prática para propagar as atualizações para outras
tabelas, ou fazer verificações de consistência em outras tabelas.
A razão para esta divisão de trabalho, é que um gatilho
AFTER pode ter certeza de que está vendo o valor
final da linha, enquanto um gatilho BEFORE não
pode; pode haver outros gatilhos BEFORE
disparando depois dele.
Caso não haja um motivo específico para usar um gatilho
BEFORE ou AFTER, a opção
BEFORE é mais eficiente, porque as informações
sobre a operação não precisam ser salvas até o final da instrução.
Se uma função de gatilho executar comandos SQL,
estes comandos poderão disparar gatilhos novamente.
Isto é conhecido como gatilhos em cascata.
Não há um limite direto no número de níveis da cascata.
É possível que cascatas causem uma chamada recursiva do mesmo gatilho;
por exemplo, um gatilho INSERT pode executar um
comando que insere uma linha adicional na mesma tabela, fazendo com
que o gatilho INSERT seja disparado novamente.
É responsabilidade do programador do gatilho evitar a recursão
infinita nesses cenários.
Se uma restrição de chave estrangeira especificar ações referenciais
(isto é, atualizações ou exclusões em cascata), estas ações serão
executadas por meio de comandos SQL
UPDATE ou DELETE comuns na
tabela de referência.
Em particular, quaisquer gatilhos existentes na tabela de referência
serão acionados para estas alterações.
Se um gatilho desse tipo modificar ou bloquear o efeito de um desses
comandos, o resultado final poderá ser a quebra da integridade
referencial.
É responsabilidade do programador do gatilho evitar isto.
Quando um gatilho está sendo definido, podem ser especificados
argumentos para ele.
O objetivo de incluir argumentos na definição do gatilho, é permitir
que diferentes gatilhos com requisitos semelhantes chamem a mesma
função.
Por exemplo, pode haver uma função de gatilho geral que usa
como argumentos dois nomes de colunas, e coloca o usuário corrente
em uma coluna, e o carimbo de data/hora corrente na outra.
Escrito corretamente, esta função de gatilho seria independente
da tabela específica em que está disparando.
Portanto, a mesma função pode ser usada para eventos
INSERT em qualquer tabela com colunas adequadas,
para rastrear automaticamente a criação de registros em uma tabela
de transações, por exemplo.
Também pode ser usada para rastrear eventos da última atualização,
se definida como um gatilho para UPDATE.
Cada linguagem de programação que oferece suporte a gatilhos tem seu
próprio método para disponibilizar os dados de entrada do gatilho
para a função de gatilho.
Estes dados de entrada incluem o tipo de evento do gatilho
(por exemplo, INSERT ou UPDATE),
bem como quaisquer argumentos listados em
CREATE TRIGGER.
Para um gatilho no nível de linha, os dados de entrada também incluem
a linha NEW para os gatilhos
INSERT e UPDATE, e/ou
OLD para os gatilhos UPDATE
e DELETE.
Por padrão, os gatilhos no nível de instrução não têm como examinar
a(s) linha(s) individual(is) modificada(s) pela instrução.
Mas um gatilho AFTER STATEMENT pode requisitar
que sejam criadas tabelas de transição para
disponibilizar os conjuntos de linhas afetadas pelo gatilho.
Os gatilhos AFTER ROW também podem solicitar
tabelas de transição, para poderem ver o total de alterações na
tabela, bem como a alteração na linha individual para a qual estão
sendo disparados no momento.
O método para examinar novamente as tabelas de transição depende da
linguagem de programação que está sendo usada, mas a abordagem
típica é fazer com que as tabelas de transição atuem como tabelas
temporárias, somente para leitura, que podem ser acessadas por
comandos SQL emitidos na função de gatilho.
[119] idempotente: que tem a propriedade de poder ser aplicado mais do que uma vez sem que o resultado se altere. in Dicionário Priberam da Língua Portuguesa. (N. T.)