Un trigger spécifie que la base de données doit exécuter automatiquement une fonction donnée chaque fois qu'un certain type d'opération est exécuté. Les fonctions trigger peuvent être attachées à une table (partitionnée ou non), une vue ou une table distante.
Sur des tables et tables distantes, les triggers peuvent être définies pour s'exécuter
avant ou après une commande INSERT
,
UPDATE
ou DELETE
, soit une fois par
ligne modifiée, soit une fois par expression SQL.
Les triggers UPDATE
peuvent en plus être
configurées pour n'être déclenchés que si certaines colonnes sont
mentionnées dans la clause SET
de l'instruction
UPDATE
. Les triggers peuvent aussi se déclencher
pour des instructions
TRUNCATE
. Si un événement d'un trigger intervient, la
fonction du trigger est appelée au moment approprié pour gérer
l'événement.
Des triggers peuvent être définies sur des vues pour exécuter des opérations
à la place des commandes INSERT
, UPDATE
ou DELETE
. Les triggers INSTEAD OF
sont déclenchés une fois par ligne devant être modifiée dans la vue. C'est
de la responsabilité de la fonction trigger de réaliser les modifications
nécessaires pour que les tables de base sous-jacentes d'une vue et, si approprié,
de renvoyer la ligne modifiée comme elle apparaîtra dans la vue. Les
triggers sur les vues peuvent aussi être définis pour s'exécuter une
fois par requête SQL statement, avant ou après des
opérations INSERT
, UPDATE
ou
DELETE
. Néanmoins, de tels triggers sont déclenchés
seulement s'il existe aussi un trigger INSTEAD OF
sur
la vue. Dans le cas contraire, toute requête ciblant la vue doit être
réécrite en une requête affectant sa (ou ses) table(s) de base. Les
triggers déclenchés seront ceux de(s) table(s) de base.
La fonction trigger doit être définie avant que le trigger lui-même
puisse être créé. La fonction trigger doit être déclarée comme une
fonction ne prenant aucun argument et retournant un type trigger
(la fonction trigger reçoit ses entrées via une structure
TriggerData
passée spécifiquement, et non pas sous la forme
d'arguments ordinaires de fonctions).
Une fois qu'une fonction trigger est créée, le trigger est créé avec CREATE TRIGGER. La même fonction trigger est utilisable par plusieurs triggers.
PostgreSQL offre des triggers
par ligne et par instruction. Avec un
trigger en mode ligne, la fonction du
trigger est appelée une fois pour chaque ligne affectée par
l'instruction qui a lancé le trigger. Au contraire, un trigger en mode
instruction n'est appelé qu'une seule fois lorsqu'une instruction appropriée
est exécutée, quelque soit le nombre de lignes affectées par cette
instruction. En particulier, une instruction n'affectant aucune ligne
résultera toujours en l'exécution de tout trigger en mode instruction
applicable. Ces deux types sont quelque fois appelés respectivement des
triggers niveau ligne et des
triggers niveau instruction.
Les triggers sur TRUNCATE
peuvent seulement être définis
au niveau instruction, et non pas au niveau ligne.
Les triggers sont aussi classifiées suivant qu'ils se déclenchent avant
(before), après (after) ou
à la place (instead of) de l'opération. Ils sont
référencés respectivement comme des triggers BEFORE
,
AFTER
et INSTEAD OF
. Les triggers
BEFORE
au niveau requête se déclenchent avant que la
requête ne commence quoi que ce soit alors que les triggers
AFTER
au niveau requête se déclenchent tout à la fin de
la requête. Ces types de triggers peuvent être définis sur les tables,
vues et tables externes. Les triggers BEFORE
au niveau ligne se déclenchent
immédiatement avant l'opération sur une ligne particulière alors que les
triggers AFTER
au niveau ligne se déclenchent à la fin
de la requête (mais avant les triggers AFTER
au niveau
requête). Ces types de triggers peuvent seulement être définis sur les
tables et sur les tables distantes, et non pas sur les vues. Les triggers
INSTEAD OF
peuvent seulement être définis sur des vues, et seulement au niveau ligne.
Ils se déclenchent immédiatement pour chaque ligne de la vue identifiée
comme nécessitant une action.
L'exécution d'un trigger AFTER
peut être reporté à la
fin de la transaction, plutôt qu'à la fin de la requête, s'il a été défini
comme un trigger de contrainte. Dans tous les cas,
un trigger est exécuté comme faisant partie de la même transaction que la
requête qui l'a exécuté, donc si soit la requête soit le trigger renvoie
une erreur, l'effet sera une annulation par ROLLBACK.
Si un INSERT
contient une clause ON CONFLICT DO
UPDATE
, il est possible pour les triggers niveau ligne
BEFORE
INSERT
puis
BEFORE
UPDATE
d'être exécutés sur
les lignes cibles. De telles interactions peuvent être complexs si les
triggers ne sont pas idempotents parce que les modifications réalisées par
les triggers BEFORE
INSERT
seront
vus par les triggers BEFORE
UPDATE
,
ceci incluant les modifications sur les colonnes
EXCLUDED
.
Notez que les triggers
UPDATE
niveau instruction sont exécutés lorsque
la clause ON CONFLICT DO UPDATE
est spécifiée,
quand bien même aucune ligne ne serait affectée par la commande
UPDATE
(et même si la commande
UPDATE
n'est pas exécutée). Une commande
INSERT
avec une clause ON CONFLICT DO
UPDATE
exécutera d'abord les triggers niveau
instruction BEFORE
INSERT
,
puis les triggers niveau instruction BEFORE
UPDATE
, suivis par les triggers niveau
instruction AFTER
UPDATE
,
puis finalement les triggers niveau instruction
AFTER
INSERT
.
Une instruction qui cible une table parent dans une hiérarchie d'héritage ou de partitionnement ne cause pas le déclenchement des triggers niveau instruction des tables enfants affectées ; seuls les triggers niveau instruction de la table parent sont déclenchés. Néanmoins, les triggers niveau ligne de toute table enfant affectée seront déclenchés.
Si un UPDATE
sur une table partitionnée implique le
déplacement d'une ligne vers une autre partition, il sera réalisé comme un
DELETE
de la partition originale, suivi d'un
INSERT
dans la nouvelle partition. Dans ce cas, les
triggers BEFORE
UPDATE
niveau ligne
et tous les triggers BEFORE
DELETE
niveau ligne sont déclenchés sur la partition originale. Puis tous les
triggers BEFORE
INSERT
niveau ligne
sont déclenchés sur la partition destination. La possibilité de résultats
surprenants doit être considéré quand tous les triggers affectent la ligne
déplacée. En ce qui concerne les triggers AFTER ROW
,
les triggers AFTER
DELETE
et
AFTER
INSERT
sont appliqués mais les
triggers AFTER
UPDATE
ne le sont pas
car UPDATE
a été convertis en un
DELETE
et un INSERT
. Quant aux
triggers niveau instruction, aucun des triggers DELETE
et INSERT
ne sont déclenchés, y compris en cas de
déplacement de lignes. Seuls les triggers UPDATE
définis sur la table cible utilisés dans une instruction
UPDATE
seront déclenchés.
Aucun trigger spécifique n'est défini pour MERGE
. À la
place, des triggers niveau instruction ou niveau ligne sont déclenchés pour
les instructions UPDATE
, DELETE
et
INSERT
suivant l'action indiquée dans la requête
MERGE
(pour les triggers niveau instruction) et suivant
les actions réellement exécutées (pour les triggers niveau instruction).
Lors de l'exécution d'une commande MERGE
, les triggers
BEFORE
and AFTER
au niveau instruction
sont déclenchées pour les événements spécifiés dans les actions de la
commande MERGE
, que l'action soit exécutée ou non au
final. C'est identique à l'instruction UPDATE
qui ne met à
jour aucune ligne, mais pour laquelle, néanmoins, les triggers niveau
instruction ont été exécutés. Les triggers niveau ligne sont déclenchés
uniquement quand une ligne est réellement mise à jour, insérée ou supprimée.
Donc il est parfaitement normal que, bien que les triggers niveau instruction
soient déclenchés pour certains types d'action, les triggers niveau ligne ne
le soient pas pour les mêmes actions.
Les fonctions triggers appelées par des triggers niveau instruction
devraient toujours renvoyer NULL
. Les fonctions triggers
appelées par des triggers niveau ligne peuvent renvoyer une ligne de la
table (une valeur de type HeapTuple
) vers
l'exécuteur appelant, s'ils le veulent. Un trigger niveau ligne exécuté
avant une opération a les choix suivants :
Il peut retourner un pointeur NULL
pour sauter l'opération
pour la ligne courante. Ceci donne comme instruction à l'exécuteur de ne pas exécuter
l'opération niveau ligne qui a lancé le trigger (l'insertion, la
modification ou la suppression d'une ligne particulière de la table).
Pour les triggers INSERT
et
UPDATE
de niveau ligne uniquement, la valeur de retour devient la
ligne qui sera insérée ou remplacera la ligne en cours de mise à jour.
Ceci permet à la fonction trigger de modifier la ligne en cours
d'insertion ou de mise à jour.
Un trigger BEFORE
niveau ligne qui ne serait pas conçu pour avoir l'un de
ces comportements doit prendre garde à retourner la même ligne que celle
qui lui a été passée comme nouvelle ligne (c'est-à-dire : pour des triggers
INSERT
et UPDATE
: la nouvelle
(NEW
) ligne, et pour les triggers
DELETE
) : l'ancienne
(OLD
) ligne .
Un trigger INSTEAD OF
niveau ligne devrait renvoyer
soit NULL
pour indiquer qu'il n'a pas modifié de données
des tables de base sous-jacentes de la vue, soit la ligne de la vue qui lui
a été passé (la ligne NEW
pour les opérations
INSERT
et UPDATE
, ou la ligne
OLD
pour l'opération DELETE
). Une
valeur de retour différent de NULL est utilisée comme signal indiquant que
le trigger a réalisé les modifications de données nécessaires dans la vue.
Ceci causera l'incrémentation du nombre de lignes affectées par la commande.
Pour les opérations INSERT
et UPDATE
seulement,
le trigger peut modifier la ligne NEW
avant de la
renvoyer. Ceci modifiera les données renvoyées par INSERT
RETURNING
ou UPDATE RETURNING
, et est utile
quand la vue n'affichera pas exactement les données fournies.
La valeur de retour est ignorée pour les triggers niveau ligne lancés
après une opération. Ils peuvent donc renvoyer la valeur
NULL
.
Certaines considérations s'appliquent pour les colonnes générées.
Les colonnes générées sont calculées après
les triggers BEFORE
et avant les triggers
AFTER
. De ce fait, la valeur générée peut être inspectée
dans les triggers AFTER
. Dans les triggers
BEFORE
, la ligne OLD
contient
l'ancienne valeur générée, comme on pourrait s'y attendre, mais la ligne
NEW
ne contient pas encore la nouvelle valeur générée et
ne doit pas être accédée. Dans l'interface en langage C, le contenu de la
colonne est non défini à ce moment ; un langage de programmation de
plus haut niveau doit empêcher l'accès à une colonne générée dans la ligne
NEW
pour un trigger BEFORE
. Les
modifications de la valeur d'une colonne générée dans un trigger
BEFORE
sont ignorées et seront écrasées.
Si plus d'un trigger est défini pour le même événement sur la même
relation, les triggers seront lancés dans l'ordre alphabétique de leur
nom. Dans le cas de triggers BEFORE
et
INSTEAD OF
, la ligne
renvoyée par chaque trigger, qui a éventuellement été modifiée, devient l'argument du
prochain trigger. Si un des triggers BEFORE
ou
INSTEAD OF
renvoie un pointeur
NULL
, l'opération est abandonnée pour cette ligne et les
triggers suivants ne sont pas lancés (pour cette ligne).
Une définition de trigger peut aussi spécifier une condition booléenne
WHEN
qui sera testée pour savoir si le trigger doit
bien être déclenché. Dans les triggers de niveau ligne, la condition
WHEN
peut examiner l'ancienne et la nouvelle valeur
des colonnes de la ligne. (les triggers de niveau instruction peuvent
aussi avoir des conditions WHEN
mais cette fonctionnalité
est moins intéressante pour elles). Dans un trigger
avant, la condition WHEN
est
évaluée juste avant l'exécution de la fonction, donc l'utilisation de
WHEN
n'est pas réellement différente du test de la même
condition au début de la fonction trigger. Néanmoins, dans un tigger
AFTER
, la condition WHEN
est
évaluée juste avant la mise à jour de la ligne et détermine si un événement
va déclencher le trigger à la fin de l'instruction. Donc, quand la
condition WHEN
d'un trigger AFTER
ne renvoie pas true, il n'est pas nécessaire de mettre en queue un
événement ou de récupérer de nouveau la ligne à la fin de l'instruction.
Ceci permet une amélioration conséquente des performances pour les
instructions qui modifient un grand nombre de lignes si le trigger a
seulement besoin d'être exécuté que sur quelques lignes. Les triggers
INSTEAD OF
n'acceptent pas les conditions
WHEN
.
Les triggers BEFORE
en mode ligne sont typiquement utilisés pour
vérifier ou modifier les données qui seront insérées ou mises à jour. Par
exemple, un trigger BEFORE
pourrait être utilisé pour insérer l'heure
actuelle dans une colonne de type timestamp
ou pour vérifier que deux
éléments d'une ligne sont cohérents. Les triggers AFTER
en mode ligne
sont pour la plupart utilisés pour propager des mises à jour vers d'autres
tables ou pour réaliser des tests de cohérence avec d'autres tables. La
raison de cette division du travail est qu'un trigger AFTER
peut être
certain qu'il voit la valeur finale de la ligne alors qu'un trigger
BEFORE
ne l'est pas ; il pourrait exister d'autres triggers BEFORE
qui seront exécutés après lui. Si vous n'avez aucune raison spéciale pour le
moment du déclenchement, le cas BEFORE
est plus efficace car l'information
sur l'opération n'a pas besoin d'être sauvegardée jusqu'à la fin du
traitement.
Si une fonction trigger exécute des commandes SQL,
alors ces commandes peuvent lancer à leur tour des triggers. On appelle ceci un
trigger en cascade. Il n'y a pas de limitation directe du nombre de
niveaux de cascade. Il est possible que les cascades causent un appel
récursif du même trigger ; par exemple, un trigger
INSERT
pourrait exécuter une commande qui insère une
ligne supplémentaire dans la même table, entraînant un nouveau lancement du
trigger INSERT
. Il est de la responsabilité du
programmeur d'éviter les récursions infinies dans de tels scénarios.
Si une clé étrangère indique des actions référentielles (autrement dit, des mises à jour ou des suppressions en cascade), ces actions seront réalisées via des commandes SQL ordinaires de mise jour ou de suppression sur la table référençante. En particulier, tout trigger qui existe sur la table référençante sera déclenché pour ces modifications. Si un trigger modifie ou bloque l'effet d'une de ces commandes, le résultat final pourrait casser l'intégrité référentielle. C'est de la responsabilité du développeur du trigger d'éviter cela.
Quand un trigger est défini, des arguments peuvent être spécifiés pour
lui. L'objectif de l'inclusion d'arguments dans la définition du
trigger est de permettre à différents triggers ayant des exigences
similaires d'appeler la même fonction. Par exemple, il pourrait y avoir une
fonction trigger généralisée qui prend comme arguments deux noms de
colonnes et place l'utilisateur courant dans l'une et un horodatage dans
l'autre. Correctement écrit, cette fonction trigger serait indépendante
de la table particulière sur laquelle il se déclenche. Ainsi, la même
fonction pourrait être utilisée pour des événements
INSERT
sur n'importe quelle table ayant des colonnes
adéquates, pour automatiquement suivre les créations d'enregistrements dans
une table de transactions par exemple. Elle pourrait aussi être utilisée
pour suivre les dernières mises à jours si elle est définie comme un
trigger UPDATE
.
Chaque langage de programmation supportant les triggers a sa propre
méthode pour rendre les données en entrée disponible à la fonction du
trigger. Cette donnée en entrée inclut le type d'événement du
trigger (c'est-à-dire INSERT
ou
UPDATE
) ainsi que tous les arguments listés dans
CREATE TRIGGER
. Pour un trigger niveau ligne, la donnée en
entrée inclut aussi la ligne NEW
pour les triggers
INSERT
et UPDATE
et/ou la ligne
OLD
pour les triggers UPDATE
et
DELETE
.
Par défaut, les triggers niveau instruction n'ont aucun moyen d'examiner le
ou les lignes individuelles modifiées par la requête. Mais un trigger
AFTER STATEMENT
peut demander que des tables
de transition soient créées pour rendre disponible les
ensembles de lignes affectées au trigger. AFTER ROW
peut
aussi demander les tables de transactions, pour accéder au changement
global dans la table, ainsi qu'au changement de lignes individuelles pour
lesquels ils ont été déclenchés. La méthode d'examen des tables de
transition dépend là-aussi du langage de programmation utilisé mais
l'approche typique est de transformer les tables de transition en tables
temporaires en lecture seule pouvant être accédées par des commandes SQL
lancées par la fonction trigger.