web-dev-qa-db-fra.com

Attribuer au nouveau par clé dans un déclencheur des postgres

Dans le corps de la gâchette, comment puis-je affecter une valeur à NEW par son nom de champ?
[.____] C'est ce que je veux faire:

some_key = "some_column";
NEW[some_key] = 5;
3
Ben

Tout d'abord, là est Pas de "corps de déclenchement" (contrairement à Oracle). Dans Postgres, vous avez une fonction de déclenchement (également, trompeuse, appelée "procédure") avec un corps de fonction et des déclencheurs 0-N (sans corps) appelant cette fonction.

La variable spéciale NEW dans la fonction de déclenchement PLPGSQL n'est ni une carte ni un tableau; c'est un record Tenir la nouvelle ligne :

NEW

Type de données RECORD; Variable tenant la nouvelle ligne de base de données pour INSERT/UPDATE Opérations dans les déclencheurs de niveau de ligne. Cette variable n'est pas affirmée dans les déclencheurs de niveau de la déclaration et pour les opérations DELETE.

L'attribution à un champ (ou à une colonne) de NEW est simple. Le documenté Opérateur d'affectation est := . (- Depuis Postgres 9.4 = est également documenté. )

NEW.some_key := 5;

Ce que vous semble rechercher, c'est paramétrer le nom de la colonne, qui n'est pas aussi simple.
Le Module supplémentaire hstore fournit le #= opérateur . (Il est inclus dans toutes les distributions standard.) Installez le module une fois par base de données avec:

CREATE EXTENSION hstore;

Ensuite vous pouvez:

NEW := NEW #= '"some_key"=>"5"'::hstore;

Définit la colonne some_key à '5' - si la colonne existe.

  • Un casting explicite à hstore est facultatif. L'opérateur #= contrainte automatiquement un littéral de chaîne au type de données de droite.
  • hstore _ stocke uniquement les chaînes de texte, donc un littéral donné pour la valeur peut avoir à être coulé deux fois - Un inconvénient très mineur par rapport aux solutions alternatives.
  • Le littéral de chaîne donné doit s'adapter au Type de données de la colonne ou une exception est soulevée.
  • Si aucune colonne avec le nom donné n'existe, rien n'est changé, aucune exception soulevée.

Réponse associée avec Détails et solution alternative:

Exemple de code

CREATE OR REPLACE FUNCTION trg_tbl_ins_bef()
  RETURNS trigger AS
$func$
BEGIN
   NEW := NEW #= '"some_key"=>"5"';
   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

CREATE TRIGGER ins_bef
BEFORE INSERT ON tbl
FOR EACH ROW EXECUTE PROCEDURE trg_tbl_ins_bef();

À Postgres 11 ou plus tard, je suggère la nouvelle syntaxe:

...
FOR EACH ROW EXECUTE FUNCTION trg_tbl_ins_bef();
13

Je dois admettre que ce n'est pas un moyen facile de la résoudre, mais au moins c'est un moyen. J'ai créé l'exemple ci-dessous comme une personne autonome, pour éviter tout encombrement avec la création de la gâchette et tel. Si vous l'utilisez dans un déclencheur, vous pouvez supprimer la déclaration et l'initialisation de p et de remplacer l'utilisation restante avec NEW.

DO $$
DECLARE p members_test; 
BEGIN
    p := (1,2,3);
    CREATE TEMP TABLE t ON COMMIT DROP AS SELECT p.*; -- a one row table holding 
                                                      -- the values of the variable

    EXECUTE format($e$UPDATE t SET %s = 43$e$, 'b'); -- this way you can access 
                                                     -- the columns dynamically

    SELECT * INTO p FROM t; -- assign the new values back to the variable
    RAISE INFO 'p: %', p;
END;
$$;

INFO:  p: (1,43,3)
2
dezso