web-dev-qa-db-fra.com

Déclenchement Postgres après insertion d'un accès NOUVEAU

J'ai un déclencheur assez 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();

Mes journaux de table sont les suivants:

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)
);

Après avoir inséré quelque chose dans dateien, j'obtiens l'erreur suivante:

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

Pourquoi ai-je eu cette erreur? J'ai regardé la documentation et il semble qu'ils utilisent le nouveau de la même manière que moi.

23
soupdiver

De la manuel fin :

36.1. Présentation du comportement de déclenchement
[...]
Pour un déclencheur de niveau ligne, les données d'entrée incluent également la ligne NEW pour les déclencheurs INSERT et UPDATE et/ou les OLD ligne pour les déclencheurs UPDATE et DELETE. Les déclencheurs au niveau de l'instruction n'ont actuellement aucun moyen d'examiner les lignes individuelles modifiées par l'instruction.

Et à partir de Procédures de déclenchement :

NEW
Type de données RECORD; variable contenant la nouvelle ligne de base de données pour les opérations INSERT/UPDATE dans les déclencheurs de niveau ligne. Cette variable est NULL dans les déclencheurs de niveau instruction et pour les opérations DELETE.

Notez ce qu'il dit sur les déclencheurs de niveau ligne et les déclencheurs de niveau instruction.

Vous avez un déclencheur au niveau de l'instruction:

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

Les déclencheurs au niveau de l'instruction sont déclenchés une fois par instruction et une instruction peut s'appliquer à plusieurs lignes, de sorte que la notion de ligne affectée (qui est ce que sont NEW et OLD environ) ne s'applique tout simplement pas.

Si vous souhaitez utiliser NEW (ou OLD) dans un déclencheur, vous voulez que le déclencheur s'exécute pour chaque ligne affectée et cela signifie que vous voulez un déclencheur de niveau ligne:

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

Je viens de changer FOR EACH STATEMENT à FOR EACH ROW.


Votre déclencheur devrait également renvoyer quelque chose :

Une fonction de déclencheur doit renvoyer NULL ou une valeur d'enregistrement/ligne ayant exactement la structure de la table pour laquelle le déclencheur a été déclenché.
[...]
La valeur de retour d'un déclencheur de niveau ligne déclenché AFTER ou d'un déclencheur de niveau instruction déclenché BEFORE ou AFTER est toujours ignoré; il pourrait tout aussi bien être nul. Cependant, l'un de ces types de déclencheurs peut encore interrompre l'opération entière en générant une erreur.

Donc tu devrais RETURN NEW; ou RETURN NULL; dans votre déclencheur. Vous avez un déclencheur AFTER donc peu importe le RETOUR que vous utilisez, mais je choisirais RETURN NEW;.

42
mu is too short