À quelle fréquence un déclencheur FOR EACH STATEMENT s'exécute-t-il si l'opération est provoquée par une contrainte FK avec UPDATE CASCADE?

11

Je comprends qu'un déclencheur sur la table t défini avec FOR EACH STATEMENTsera exécuté une fois lorsque j'exécuterai un UPDATE t ....

Maintenant, quand test défini avec FOREIGN KEY ... REFERENCES a ... ON UPDATE CASCADE, et je mets à jour N lignes a, cela provoquera-t-il l'appel du déclencheur une fois, ou N fois?

Autrement dit, les modifications apportées à une table en cascade par une contrainte FK ressemblent-elles plus à un simple UPDATE, ou plutôt à une série de UPDATEs?

Hanno Fietz
la source
4
Vous pourriez créer un cas de test! Insérez dans une autre table dans le corps du déclencheur et voyez combien de lignes vous obtenez. Ensuite, écrivez-le dans votre propre réponse à cette question (c'est permis, même encouragé)!
Colin 't Hart
2
La phrase principale mentionnant FOR EACH STATEMENTest orthogonale au reste de la question. Les contraintes FK sont implémentées avec des déclencheurs spéciaux FOR EACH ROW.
Erwin Brandstetter
1
@Erwin "POUR CHAQUE RANG d'un" ou "POUR CHAQUE RANG de t"?
ypercubeᵀᴹ
@ypercube: J'ai ajouté une réponse avec des détails.
Erwin Brandstetter

Réponses:

6

Les contraintes de clé étrangère sont actuellement implémentées avec des déclencheurs internes spéciaux. Tous sont exécutés FOR EACH ROW.

Notez que ce sont des détails d'implémentation qui peuvent changer, alors ne vous y fiez pas. Mais les bases n'ont pas changé au cours des deux dernières versions principales, donc des changements majeurs sont peu probables.

J'ai effectué un test rapide avec une simple contrainte FK de tblà tbltype. Un FK simple est implémenté avec quatre déclencheurs internes simples FOR EACH ROWdans mon test à la page 9.4.
Voici un bref aperçu sur la façon d'enquêter:

SELECT oid  -- 74791
FROM   pg_constraint
WHERE  conrelid = 'tbl'::regclass
AND    contype = 'f';

SELECT objid, classid::regclass  -- 74792,74793,74794,74795 / 'pg_trigger'
FROM   pg_depend
WHERE  refobjid = 74791
AND   deptype = 'i'

SELECT tgrelid::regclass, tgname, tgfoid, tgtype FROM pg_trigger
WHERE  oid IN (74792,74793,74794,74795) ORDER BY tgfoid;

'tbl'    ;'RI_ConstraintTrigger_c_74794';1644;5
'tbl'    ;'RI_ConstraintTrigger_c_74795';1645;17
'tbltype';'RI_ConstraintTrigger_a_74792';1654;9
'tbltype';'RI_ConstraintTrigger_a_74793';1655;17

SELECT oid, proname FROM pg_proc
WHERE oid IN (1654,1655,1644,1645);

1644;'RI_FKey_check_ins'
1645;'RI_FKey_check_upd'
1654;'RI_FKey_noaction_del'
1655;'RI_FKey_noaction_upd'

Deux déclencheurs "noaction" internes sont activés tbltype.
Deux déclencheurs de «vérification» internes sont activés tbl.
Tous sont exécutés FOR EACH ROW, comme indiqué par des nombres impairs dans tgtype.

Les 2 octets de Postgres tgtype smallintreprésentent un int16code source en C où le bit le moins significatif encode TRIGGER_TYPE_ROW. Explication détaillée ici:

Vous pouvez facilement tester cela avec une paire de déclencheurs identiques où vous modifiez FOR ROW/ STATEMENT...

Erwin Brandstetter
la source
5

Il s'exécute N fois, et la façon la plus simple de voir cela est d'exécuter l'instruction avec un EXPLAIN ANALYZEpréfixe, c'est-à-dire

EXPLAIN ANALYZE UPDATE a SET col = 1 WHERE othercol = 'foo';

Cela vous donnera des informations similaires à ceci:

Trigger for constraint t_col_fk on a: time=1.300 calls=9

(testé avec 9.2)

Hanno Fietz
la source
1
C'est quelque peu surprenant pour moi. Testé en 9.4 avec les mêmes résultats.
dezso