PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Endlosschleife in Triggerfunktion

Das ELSE Niederlassung radikal vereinfacht werden. Aber ein paar weitere Dinge sind ineffizient / ungenau / gefährlich:

CREATE OR REPLACE FUNCTION sample_trigger_func()
  RETURNS TRIGGER AS
$func$
BEGIN
   IF TG_OP = 'DELETE' THEN
      RAISE INFO 'OLD: %', OLD.name;

      EXECUTE format('INSERT INTO %I SELECT ($1).*', TG_TABLE_NAME || '_deletes')
      USING OLD #= hstore('{mod_op, mod_datetime}'::text[]
                         , ARRAY[left(TG_OP, 1), now()::text]);
      RETURN OLD;
   ELSE  -- insert, update
      NEW.mod_op       := left(TG_OP, 1);
      NEW.mod_datetime := now();

      RETURN NEW;
   END IF;
END
$func$  LANGUAGE plpgsql;
  • Im ELSE Verzweigung einfach NEW zuweisen direkt. Keine Notwendigkeit für mehr dynamisches SQL, das denselben Trigger erneut auslösen und eine Endlosschleife verursachen würde. Das ist der Hauptfehler.

  • RETURN NEW; außerhalb des IF Konstrukt würde Ihre Triggerfunktion für DELETE unterbrechen , seit NEW ist für DELETES nicht zugewiesen.

  • Ein Schlüsselmerkmal ist die Verwendung von hstore und der hstore-Operator #= um zwei ausgewählte Felder des bekannten Zeilentyps dynamisch zu ändern - das ist unbekannt zum Zeitpunkt des Schreibens des Codes. Auf diese Weise manipulieren Sie den ursprünglichen OLD nicht Wert, der einen überraschenden Nebeneffekt haben könnte, wenn Sie mehr Auslöser in der Ereigniskette haben.

    OLD #= hstore('{mod_op, mod_datetime}'::text[]
                 , ARRAY[left(TG_OP, 1), now()::text]);
    

    Das Zusatzmodul hstore muss installiert werden. Einzelheiten:

    Verwenden des hstore(text[], text[]) Variante hier, um einen hstore zu erstellen Wert mit mehreren Feldern im laufenden Betrieb.

  • Der Zuweisungsoperator in plpgsql ist := :

  • Beachten Sie, dass ich den Spaltennamen mod_datetime verwendet habe anstelle des irreführenden mod_date , da die Spalte offensichtlich ein timestamp ist und kein date .

Ich habe ein paar andere Verbesserungen hinzugefügt, während ich gerade dabei bin. Und der Trigger selbst sollte so aussehen:

CREATE TRIGGER insupdel_bef
BEFORE INSERT OR UPDATE OR DELETE ON table_name
FOR EACH ROW EXECUTE PROCEDURE sample_trigger_func();

SQL-Fiddle.