Pg_trigger_depth () est-il mauvais à utiliser pour empêcher la cascade de déclencheurs (récursivité)?

8

Pourquoi est-il pg_trigger_depth() = 0mauvais d'utiliser (pour autre chose que le débogage) lors de la prévention de la cascade de déclencheurs (récursivité)?

Quelqu'un peut-il fournir du code pour montrer pourquoi il est mauvais?

Je suppose que si plusieurs déclencheurs fonctionnent sur les mêmes données en même temps, une condition qui arrête un déclencheur à l'aide pg_trigger_depth() = 0arrêtera tout déclencheur qui est second en ligne pour fonctionner.

Je pensais que ce serait une bonne solution à cette (ma) question ici, mais on me dit le contraire:

Je pensais que cela ferait une bonne question.

Il est proposé ici comme solution:

Documentation Postgres 9.3:

https://www.postgresql.org/docs/9.3/static/functions-info.html

alpiniste
la source

Réponses:

1

Oui, il est toujours mauvais de dépendre d'un comportement pg_trigger_depth()

Je suis peut-être un peu moins opposé aux déclarations générales, mais à quoi cela pourrait-il servir? Je ne vois aucun argument pour expliquer pourquoi vous souhaiteriez une telle fonctionnalité. Le but principal d'une base de données est d'assurer l'intégrité des données. Et pour autant que je puisse voir, et je peux me tromper, une telle utilisation est toujours une violation potentielle de l'intégrité des données. Dans la réponse de @ Erwin, il dit "si vous oubliez" . Le but de garantir l'intégrité est d'éliminer cette possibilité. Si un agent peut se souvenir de tout et comprendre les conséquences et le code qui les entoure, vous pouvez obtenir l'intégrité des données de tout .

Introduisons quelques termes, dans la programmation, nous avons

  • "état" qui comprend tout ce à quoi un programmeur a accès.
  • "contexte d'exécution" qui inclut l'environnement d'exécution.

Nous avons en outre un terme pour une fonction, qui n'a pas d'état que nous appelons une fonction pure ,

La fonction évalue toujours la même valeur de résultat pour les mêmes valeurs d'argument. La valeur de résultat de la fonction ne peut dépendre d'aucune information ou état caché susceptible de changer au cours de l'exécution du programme ou entre différentes exécutions du programme, ni de toute entrée externe des périphériques d'E / S (généralement - voir ci-dessous).

La distinction pour la pureté est utile car elle élimine toute charge de se souvenir de quoi que ce soit au nom de l'ordinateur ou du programmeur: f(x) = yc'est toujours vrai. Ici, vous violez la pureté au pire endroit - la base de données. Et, vous le faites d'une manière complexe et sujette aux erreurs - faisant du contexte d'exécution interne une chose palpable dans l'état de votre application DB.

Je ne voudrais pas ça. Considérez simplement les complexités que vous devez tamponner mentalement.

  • Table Ales Trigger Aincendies
    • Trigger Adélivre toujours DML à Table B, tir Trigger B.
      • Trigger Bémet conditionnellement DML à Table A, tir Trigger A.
      • Trigger Bdélivre toujours DML à Table A, tir Trigger A.
    • Trigger Aémet conditionnellement DML à Table B, tir Trigger B.
      • Trigger Bémet conditionnellement DML à Table A, tir Trigger A.
      • Trigger Bdélivre toujours DML à Table A, tir Trigger A.

Si cela semble complexe, gardez à l'esprit que "conditionnellement", il peut être étendu à "cela s'est produit, mais cela ne se produit pas toujours" et "cela ne s'est pas produit, mais cela peut se produire ailleurs".

Pour pg_trigger_depth()même être utile, vous devez avoir une série d'événements qui est tout aussi complexe. Et, maintenant, vous voulez que l'exécution dépende du contenu d'exécution dans cette chaîne?

J'éviterais ça. Si votre système de déclenchement est aussi complexe, vous jouez de la patate chaude avec une grenade à main dans un placard tout seul et il ne semble pas susceptible de bien se terminer.

Evan Carroll
la source
10

Il n'est pas mauvais d'utiliser en soi (à mon humble avis). Vous avez juste besoin d'être conscient des implications.

Je préfère le faire pg_trigger_depth() < 1au lieu de pg_trigger_depth() = 0, sur le principe, mais ce n'est pas important ici. Nous parlons de déclencheurs comme:

CREATE TRIGGER my_trigger
AFTER INSERT ON my_tbl
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)
EXECUTE PROCEDURE my_trigger_func();

Si vous ajoutez plus tard des déclencheurs qui déclencheraient à leur tour votre déclencheur, rien ne se produira à la place. Votre déclencheur ne réagit qu'aux événements non déclencheurs. En fonction de votre configuration, cela peut être exactement ce que vous voulez - ou une sorte de piège, si vous oubliez cela et ajoutez plus tard d'autres déclencheurs qui devraient à leur tour (également) déclencher ce déclencheur. Vous modifiez le comportement par défaut. Assurez-vous de documenter cela clairement.

Sachez que pg_trigger_depth()compte n'importe quel déclencheur , pas seulement le même déclencheur pour empêcher la récursivité, de sorte que la condition empêche le déclencheur de se déclencher s'il est également appelé à partir d'un autre déclencheur.
Le manuel:

niveau d'imbrication actuel des déclencheurs PostgreSQL (0 s'il n'est pas appelé, directement ou indirectement, depuis l'intérieur d'un déclencheur)

En relation:

Erwin Brandstetter
la source