Desencadenador de Postgres después de insertar acceso NUEVO

Tengo un disparador bastante simple:

CREATE OR REPLACE FUNCTION f_log_datei()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id);
END; $$ LANGUAGE 'plpgsql';

CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();

Los registros de mi tabla son los siguientes:

CREATE TABLE logs(
    id int PRIMARY KEY DEFAULT NEXTVAL('logs_id_seq'),
    zeit timestamp DEFAULT now(),
    aktion char(6),
    tabelle varchar(32),
    alt varchar(256),
    neu varchar(256),
    benutzer_id int references benutzer(id)
);

Después de insertar algo en dateien, aparece el siguiente error:

ERROR:  record "new" is not assigned yet
DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT:  SQL statement "INSERT INTO logs (aktion, tabelle, benutzer_id) VALUES(TG_OP, 'dateien', NEW.benutzer_id)"
PL/pgSQL function "f_log_datei" line 3 at SQL statement

¿Por qué recibí este error? Revisé la documentación y parece que usan new de la misma manera que yo.

preguntado el 12 de junio de 12 a las 17:06

1 Respuestas

Desde el fino manual:

36.1. Descripción general del comportamiento del disparador
[...]
Para un disparador de nivel de fila, los datos de entrada también incluyen el NEW fila para INSERT y UPDATE disparadores, y/o el OLD fila para UPDATE y DELETE disparadores Actualmente, los disparadores a nivel de declaración no tienen ninguna forma de examinar las filas individuales modificadas por la declaración.

Y de Procedimientos de activación:

NEW
Tipo de datos RECORD; variable que contiene la nueva fila de la base de datos para INSERT/UPDATE operaciones en activadores de nivel de fila. Esta variable es NULL en disparadores a nivel de declaración y para DELETE operaciones.

Tenga en cuenta lo que dice sobre los disparadores de nivel de fila y los disparadores de nivel de declaración.

Tiene un disparador a nivel de declaración:

...
FOR EACH STATEMENT
EXECUTE PROCEDURE f_log_datei();

Los disparadores de nivel de declaración se activan una vez por declaración y una declaración se puede aplicar a varias filas, por lo que la noción de fila afectada (Que es que NEW y OLD están a punto) simplemente no se aplica.

Si quieres usar NEW (o OLD) en un disparador, quiere que el disparador se ejecute para cada fila afectada y eso significa que quiere un disparador a nivel de fila:

CREATE TRIGGER log_datei AFTER INSERT OR UPDATE OR DELETE
ON dateien
FOR EACH ROW
EXECUTE PROCEDURE f_log_datei();

Acabo de cambiar FOR EACH STATEMENT a FOR EACH ROW.


Su disparador también debe estar devolviendo algo:

Una función de disparo debe devolver NULL o un valor de registro/fila que tenga exactamente la estructura de la tabla para la que se activó el disparador.
[...]
El valor de retorno de un activador de nivel de fila activado AFTER o un disparador de nivel de declaración disparado BEFORE or AFTER siempre se ignora; bien podría ser nulo. Sin embargo, cualquiera de estos tipos de activadores podría anular toda la operación al generar un error.

Entonces deberías RETURN NEW; or RETURN NULL; en tu gatillo. Tiene un disparador DESPUÉS, por lo que no importa qué RETORNO use, pero yo iría con RETURN NEW;.

Respondido el 12 de junio de 12 a las 19:06

¿También agregaste RETURN NEW; al final de la función? - kgrittn

@kgrittn: Ese sería el siguiente bicho, no? Pero sí, ni siquiera vi eso y vale la pena mencionarlo mientras estamos aquí. - mu es demasiado corto

sí, he agregado el RETURN declaración :) - buceador

Si es DESPUÉS del disparador, debería estar usando RETURN NULL. postgresql.org/docs/current/plpgsql-trigger.html - Anacardos

Porque el gatillo AFTER no tiene efecto. regresando NEW es engañoso en el mejor de los casos. - Anacardos

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.