sql >> Base de Datos >  >> RDS >> PostgreSQL

Bucle sin fin en la función de disparo

El ELSE La rama se puede simplificar radicalmente. Pero un par de cosas más son ineficientes/inexactas/peligrosas:

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;
  • En el ELSE rama simplemente asigne a NEW directamente. No es necesario un SQL más dinámico, que dispararía el mismo activador nuevamente y provocaría un bucle sin fin. Ese es el error principal.

  • RETURN NEW; fuera del IF construir rompería su función de activación para DELETE , desde NEW no está asignado para DELETES.

  • Una característica clave es el uso de hstore y el operador hstore #= para cambiar dinámicamente dos campos seleccionados del tipo de fila conocido - eso es desconocido en el momento de escribir el código. De esta forma, no alterará el OLD original. valor, que podría tener efectos secundarios sorprendentes si tiene más factores desencadenantes en la cadena de eventos.

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

    El módulo adicional hstore debe ser instalado. Detalles:

    Usando el hstore(text[], text[]) variante aquí para construir un hstore valor con múltiples campos sobre la marcha.

  • El operador de asignación en plpgsql es := :

  • Tenga en cuenta que usé el nombre de columna mod_datetime en lugar del engañoso mod_date , ya que la columna es obviamente una timestamp y no una date .

Agregué un par de otras mejoras mientras estaba en eso. Y el activador en sí debería verse así:

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

SQL Fiddle.