Mittwoch, Dezember 14, 2011

Ehrenrettung für Trigger

Ich bin bei Tom Kyte (und anderswo) so oft vor dem Einsatz von Triggern gewarnt worden und habe selbst so viele Unerfreuliches mit ihnen erlebt, dass ich sehr gespannt darauf bin, was Toon Koppelaars in seinem neuen Blog Triggers Considered Harmful, Considered Harmful alles aufführen wird, um den schlechten Ruf dieses Features zu korrigieren.

Hier eine kurze Liste der bisher veröffentlichten Artikel (die ich vermutlich nicht bis zum Ende des Blogs ergänzen werde):
  • Starting this blog: mit einem Blick zurück ins Jahr 1994 und einigen Tom-Kyte-Zitaten zum Thema Trigger.
  • So what triggers are we talking about? mit einer kurzen Klassifizierung nach Trigger-Typen und einer Erläuterung zu row-level- und statement-level-triggers.
  • Some preliminaries: erklärt, dass in einem Trigger keine DDL-Operation aufgerufen werden darf (wegen des impliziten Commit; wenn man den problematischen Code in eine autonome Transaktion packt, kann diese die Änderungen der laufenden DML-Operation dann - natürlich - nicht sehen). Dann wird erklärt, dass der Fehlschlag eines Triggers ein Statement-Rollback für die betroffene DML-Operation hervorruft.Außerdem wird das mutating table Problem erläutert: "row level triggers are not allowed to read what's called the mutating table. If your row level trigger code queries the table that's being affected by the triggering DML statement, then Oracle will throw an ORA-04091 at you and cause the above mentioned statement level rollback."
  • "Workarounds" for ORA-04091: nennt zwei beliebte Workarounds für das Mutating-Table-Problem: die Verwendung einer autonomen Transaktion und die (weit weniger verbreitete) Verwendung eines loopback db-link. Ein wichtiger Unterschied der beiden Varianten ist, dass der db-link-Zugriff die Änderungen der noch nicht festgeschriebenen Transaktion sehen kann (da er zur gleichen dirstibuted transaction gehört), während die autonomous transaction das (natürlich) nicht kann.
  • The mutating table error prevents non-deterministic behavior of your code: erklärt, warum die beiden oben angeführten Workarounds grundsätzlich problematisch sind - und wieso der Mutating-Table-Error eine sinnvolle Sicherheitseinrichtung ist: im Fall des Inserts mehrerer Sätze entscheidet die Reihenfolge der rows darüber, ob ein row Trigger mit loopback db_link, der die Beziehung von Tabelleninhalten überprüft, einen Fehler liefert oder nicht - das Verhalten ist also nicht deterministisch. Ein row Trigger mit dem Pragma autonomous transaction würde in einem solchen Fall immer einen Fehler liefern, wenn die neu eingefügten Sätze nur in ihrer Kombination die Logik des Triggers erfüllen.
  • Look mom: a mutating table error without a trigger!: zeigt dass ORA-04091 nicht nur für Trigger relevant ist, sondern auch für Funktionen - und auch dort einen wichtigen Sicherheitsmechanismus darstellt.
  • hier fehlen ein oder zwei Artikel ...
  • Where TK agrees with TK (or: why are triggers harmful): liefert Beispiele für Trigger-Verwendungen, die auch der Herr  Koppelaars für problematisch hält, da sie das Standardverhalten von DML-Operationen "automagically" verändern (wie's der Herr Kyte nennt). Die fraglichen Bereiche sind unter anderem die Ergänzung von Spalten-Werten (z.B. durch automatisches Setzen eines INSERT_DATE), die konditionale Ausführung weiterer DML-Operationen (wenn ein Satz in Tabelle A eingefügt wird, erfolgt automatisch ein weiteres INSERT nach B) und die Ausführung nicht-transaktionaler Operationen (z.B. autonomer Transaktionen).
  • The fourth use-case for triggers: beschäftigt sich mit Triggern, die Select-Statements ausführen - in erster Linie, um Datenintegritäts-Constraints zu unterstüzen (obwohl Tom Kyte davon explizit abrät). Dabei erfolgt ein Hinweis auf die ASSERTION, die Bestandteil des SQL-92-Standards ist und über die sich deklarative Datenintegritäts-Constraints definieren ließen - was daran scheitert, dass sie von keinem RDBMS-Vendor implementiert wurde, da das offenbar alles andere als trivial wäre.
  • Continuing the story: liefert zunächst eine Zusammenfassung der zuvor veröffentlichten Artikel, die die Grundidee des jeweiligen Textes (aus naheliegenden Gründen) sehr viel treffender beschreibt, als ich das hier getan bzw. versucht habe. Die Liste ist außerdem auch lückenlos, was sie ebenfalls von meiner Sammlung unterscheidet. Darüber hinaus liefert der Artikel die Antwort auf die der gesamten Serie zugrunde liegende Frage: wie vermeidet man den mutating table error bei der Implementierung eines multi-row constraints sinnvoll (also nicht mit den problematischen Krücken loopback DB-Link oder autonomous transaction)? Diese Antwort lautet: man muss eine Kombination aus row-level und statement-level trigger verwenden. Die Lösung ist ein wenig komplexer, als dass ich sie hier nacherzählen wollte - aber an ihre Existenz sollte ich mich erinnern, wenn ich multi-row sonstraints definieren müsste.

Keine Kommentare:

Kommentar veröffentlichen