Ist pg_trigger_depth () schlecht, um eine Trigger-Kaskadierung (Rekursion) zu verhindern?

8

Warum ist es pg_trigger_depth() = 0schlecht zu verwenden (für etwas anderes als das Debuggen), um eine Trigger-Kaskadierung (Rekursion) zu verhindern?

Kann jemand Code bereitstellen, um zu demonstrieren, warum er schlecht ist?

Ich vermute, denn wenn mehrere Trigger gleichzeitig an denselben Daten arbeiten, stoppt eine Bedingung, die einen Trigger mit pg_trigger_depth() = 0stoppt, jeden Trigger, der an zweiter Stelle in der Reihe steht, um zu arbeiten.

Ich dachte, es wäre eine gute Lösung für diese (meine) Frage hier, aber mir wird etwas anderes gesagt:

Ich dachte, es wäre eine gute Frage.

Es wird hier als Lösung angeboten:

Dokumentation zu Postgres 9.3:

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

Bergsteiger
quelle

Antworten:

1

Ja, es ist immer schlecht, Verhalten abhängig zu machen pg_trigger_depth()

Vielleicht bin ich pauschalen Aussagen etwas weniger abgeneigt, aber was könnte es nützen? Es gibt kein Argument dafür, warum Sie eine solche Funktion wünschen würden. Der Hauptzweck einer Datenbank besteht darin, die Datenintegrität sicherzustellen. Und soweit ich sehen kann und mich möglicherweise irre, ist eine solche Verwendung immer eine potenzielle Verletzung der Datenintegrität. In @ Erwins Antwort sagt er "wenn du es vergisst" . Der Zweck der Gewährleistung der Integrität besteht darin, diese Möglichkeit auszuschließen. Wenn sich ein Agent an alles erinnern und die Konsequenzen und den Code um ihn herum verstehen kann, können Sie die Datenintegrität aus allem herausholen .

Lassen Sie uns ein paar Begriffe in die Programmierung einführen, die wir haben

  • "state", der alles enthält , auf das ein Programmierer Zugriff hat.
  • "Ausführungskontext", der die Ausführungsumgebung enthält.

Wir haben weiterhin einen Begriff für eine Funktion, die keinen Zustand hat, den wir als reine Funktion bezeichnen .

Die Funktion wertet immer den gleichen Ergebniswert bei gleichen Argumentwerten aus. Der Funktionsergebniswert kann weder von versteckten Informationen oder Zuständen abhängen, die sich während der Programmausführung oder zwischen verschiedenen Programmausführungen ändern können, noch von externen Eingaben von E / A-Geräten (normalerweise - siehe unten).

Die Unterscheidung nach Reinheit ist nützlich, da sie die Belastung beseitigt, sich im Namen des Computers oder des Programmierers an etwas zu erinnern: f(x) = yist immer wahr. Hier verletzen Sie die Reinheit am schlimmsten Ort - der Datenbank. Und Sie tun dies auf eine komplexe und fehleranfällige Weise, sodass der interne Ausführungskontext im Status Ihrer DB-Anwendung greifbar wird.

Das würde ich nicht wollen. Denken Sie nur an die Komplexität, die Sie mental abpuffern müssen.

  • Table A's Trigger AFeuer
    • Trigger Agibt immer DML aus Table B, feuert Trigger B.
      • Trigger BBedingt DML ausgeben Table A, feuern Trigger A.
      • Trigger Bgibt immer DML aus Table A, feuert Trigger A.
    • Trigger ABedingt DML ausgeben Table B, feuern Trigger B.
      • Trigger BBedingt DML ausgeben Table A, feuern Trigger A.
      • Trigger Bgibt immer DML aus Table A, feuert Trigger A.

Wenn das komplex aussieht, denken Sie daran, dass "bedingt" weiter erweitert werden kann auf "es ist passiert, aber es kann nicht immer passieren" und "es ist nicht passiert, aber es kann woanders passieren".

Um pg_trigger_depth()überhaupt nützlich zu sein, müssen Sie eine Reihe von Ereignissen haben, die ähnlich komplex sind. Und jetzt möchten Sie, dass die Ausführung vom Ausführungsinhalt in dieser Kette abhängt?

Ich würde das vermeiden. Wenn Ihr Auslösesystem so komplex ist, spielen Sie allein mit einer Handgranate in einem Schrank heiße Kartoffeln und es scheint nicht gut zu enden.

Evan Carroll
quelle
10

Es ist nicht schlecht, per se zu verwenden (IMHO). Sie müssen sich nur der Auswirkungen bewusst sein.

Ich würde lieber mache es pg_trigger_depth() < 1statt pg_trigger_depth() = 0, auf Haupt, aber das ist nicht wichtig. Wir sprechen über Auslöser wie:

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

Wenn Sie später Auslöser hinzufügen, die wiederum Ihren Auslöser auslösen würden, geschieht stattdessen nichts. Ihr Trigger reagiert nur auf Ereignisse ohne Trigger. Abhängig von Ihrem Setup kann dies genau das sein, was Sie wollen - oder eine Art Falle, wenn Sie dies vergessen und später weitere Trigger hinzufügen, die wiederum (auch) diesen Trigger auslösen sollten. Sie ändern das Standardverhalten. Stellen Sie sicher, dass Sie dies klar dokumentieren.

Beachten Sie, dass pg_trigger_depth()Zählungen jeden Auslöser , nicht nur die gleichen Trigger - Rekursion zu verhindern, so dass die Bedingung verhindert , dass der Auslöser von Brennen , wenn aus einem anderen Trigger genannt, zu.
Das Handbuch:

aktuelle Verschachtelungsstufe von PostgreSQL-Triggern (0, wenn nicht direkt oder indirekt aus einem Trigger heraus aufgerufen)

Verbunden:

Erwin Brandstetter
quelle