Un déclencheur 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 déclencheur 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 déclencheur doit être définie avant que le déclencheur lui-même
puisse être créé. La fonction déclencheur doit être déclarée comme une
fonction ne prenant aucun argument et retournant un type trigger
(la fonction déclencheur 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 déclencheur est créée, le déclencheur (trigger) est créé avec CREATE TRIGGER. La même fonction déclencheur est utilisable par plusieurs déclencheurs.
PostgreSQL offre des déclencheurs
par ligne et par instruction. Avec un
déclencheur mode ligne, la fonction du
déclencheur est appelée une fois pour chaque ligne affectée par
l'instruction qui a lancé le déclencheur. Au contraire, un déclencheur 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 déclencheur mode instruction
applicable. Ces deux types sont quelque fois appelés respectivement des
déclencheurs niveau ligne et des
déclencheurs 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
de niveau ligne BEFORE
ne peuvent pas être définis sur
des tables partitionnées. 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.
Une instruction qui cible une table parent dans un héritage ou une hiérarchie de partitionnement ne cause pas le déclenchement des tiggers au niveau requête des tables filles affectées. Seuls les triggers au niveau requête de la table parent sont déclenchés. Néanmoins, les triggers niveau ligne de toute table fille affecté seront déclenchés.
Si une commande INSERT
contient une clause
ON CONFLICT DO UPDATE
, il est possible que les
effets des déclencheurs niveau ligne
BEFORE
INSERT
et
BEFORE
UPDATE
puissent être
tous les deux appliqués de telle sorte que leurs effets soient
visibles dans la version finale de la ligne mise à jour, si une
colonne EXCLUDED
est référencée. Il n'est
néanmoins pas nécessaire qu'il soit fait référence à une colonne
EXCLUDED
pour que les deux types de déclencheurs
BEFORE s'exécutent tout de même. La possibilité d'avoir des
résultats surprenants devrait être prise en compte quand il existe
des déclencheurs niveau ligne BEFORE
INSERT
et BEFORE
UPDATE
qui tous les deux modifient la ligne sur le
point d'être insérée ou mise à jour (ceci peut être problématique
si les modifications sont plus ou moins équivalentes et si elles ne
sont pas idempotente). Notez que les déclencheurs
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 déclencheurs niveau
instruction BEFORE
INSERT
,
puis les déclencheurs niveau instruction BEFORE
UPDATE
, suivis par les déclencheurs niveau
instruction AFTER
UPDATE
,
puis finalement les déclencheurs niveau instruction
AFTER
INSERT
.
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.
Les fonctions déclencheurs appelées par des déclencheurs niveau instruction
devraient toujours renvoyer NULL
. Les fonctions déclencheurs
appelées par des déclencheurs niveau ligne peuvent renvoyer une ligne de la
table (une valeur de type HeapTuple
) vers
l'exécuteur appelant, s'ils le veulent. Un déclencheur 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 déclencheur (l'insertion, la
modification ou la suppression d'une ligne particulière de la table).
Pour les déclencheurs 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 déclencheur de modifier la ligne en cours
d'insertion ou de mise à jour.
Un déclencheur 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 déclencheurs
INSERT
et UPDATE
: la nouvelle
(NEW
) ligne, et pour les déclencheurs
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 déclencheurs niveau ligne lancés
après une opération. Ils peuvent donc renvoyer la valeur
NULL
.
Si plus d'un déclencheur est défini pour le même événement sur la même
relation, les déclencheurs seront lancés dans l'ordre alphabétique de leur
nom. Dans le cas de déclencheurs BEFORE
et
INSTEAD OF
, la ligne
renvoyée par chaque déclencheur, qui a éventuellement été modifiée, devient l'argument du
prochain déclencheur. Si un des déclencheurs BEFORE
ou
INSTEAD OF
renvoie un pointeur
NULL
, l'opération est abandonnée pour cette ligne et les
déclencheurs 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 déclencheurs 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 déclencheur 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 déclencheurs 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 déclencheur AFTER
peut être
certain qu'il voit la valeur finale de la ligne alors qu'un déclencheur
BEFORE
ne l'est pas ; il pourrait exister d'autres déclencheurs 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 déclencheur exécute des commandes SQL,
alors ces commandes peuvent lancer à leur tour des déclencheurs. On appelle ceci un
déclencheur 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 déclencheur ; par exemple, un déclencheur
INSERT
pourrait exécuter une commande qui insère une
ligne supplémentaire dans la même table, entraînant un nouveau lancement du
déclencheur INSERT
. Il est de la responsabilité du
programmeur d'éviter les récursions infinies dans de tels scénarios.
Quand un déclencheur est défini, des arguments peuvent être spécifiés pour
lui. L'objectif de l'inclusion d'arguments dans la définition du
déclencheur est de permettre à différents déclencheurs ayant des exigences
similaires d'appeler la même fonction. Par exemple, il pourrait y avoir une
fonction déclencheur 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 déclencheur 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
déclencheur UPDATE
.
Chaque langage de programmation supportant les déclencheurs a sa propre
méthode pour rendre les données en entrée disponible à la fonction du
déclencheur. Cette donnée en entrée inclut le type d'événement du
déclencheur (c'est-à-dire INSERT
ou
UPDATE
) ainsi que tous les arguments listés dans
CREATE TRIGGER
. Pour un déclencheur niveau ligne, la donnée en
entrée inclut aussi la ligne NEW
pour les déclencheurs
INSERT
et UPDATE
et/ou la ligne
OLD
pour les déclencheurs 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.