PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 16.10 » Programmation serveur » Triggers (triggers) » Aperçu du comportement des triggers

39.1. Aperçu du comportement des triggers #

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, quel que 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 parfois 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.

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 triggers 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 triggers 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 triggers 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 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.

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Ă©e 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.

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 Ă  jour si elle est dĂ©finie comme un trigger UPDATE.

Chaque langage de programmation supportant les triggers à 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.