PostgreSQL Antipatterns: modification des données en contournant un déclencheur

Tôt ou tard, beaucoup sont confrontés à la nécessité de corriger massivement quelque chose dans les entrées du tableau. J'ai déjà dit comment le faire mieux et comment - il vaut mieux ne pas le faire. Aujourd'hui, je vais parler du deuxième aspect de la mise à jour en masse - le déclenchement des déclencheurs .

Par exemple, sur une table dans laquelle vous devez corriger quelque chose, un mauvais déclencheur se bloque ON UPDATE, transférant toutes les modifications à certains agrégats. Et vous devez tout mettre à jour (initialiser un nouveau champ, par exemple) si soigneusement que ces unités ne sont pas affectées.

Disons simplement désactiver les déclencheurs!


BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; --  -
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

En fait, c'est tout - tout est déjà suspendu .

Parce qu'il ALTER TABLEimpose un verrou AccessExclusive , sous lequel personne ne s'exécutant en parallèle, même un simple SELECT, ne peut rien lire de la table. Autrement dit, jusqu'à ce que cette transaction soit terminée, tous ceux qui veulent «simplement lire» attendront. Et nous nous souvenons que UPDATEnous do-o-olgi ...

Arrêtons-le rapidement, puis rallumons-le rapidement!


BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;

UPDATE ...;

BEGIN;
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Ici, la situation est meilleure, le temps d'attente est beaucoup plus court. Mais seulement deux problèmes gâchent toute la beauté:

  • ALTER TABLE il attend toutes les autres opérations sur la table, y compris les longues SELECT
  • Tant que la gâchette est désactivée, tout changement dans la table passera , pas même le nôtre. Et bien, il n'entrera pas dans les unités, bien qu'il le devrait. Difficulté!

Gestion des variables de session


Ainsi, dans la version précédente, nous sommes tombés sur un point crucial - nous devons en quelque sorte enseigner le déclencheur pour distinguer «nos» changements dans le tableau de «pas les nôtres». Ignorez «le nôtre» tel quel, mais «pas le nôtre» - déclencheur. Vous pouvez utiliser des variables de session pour cela .

session_replication_role


Lisez le manuel :
La variable de déclenchement est également affectée par la variable de configuration session_replication_role . Les déclencheurs activés sans instructions supplémentaires (par défaut) se déclenchent lorsque le rôle de réplication est «origine» (par défaut) ou «local». Les déclencheurs activés par une indication ENABLE REPLICAne se déclencheront que si le mode de session actuel est "réplique", et les déclencheurs activés par une indication ENABLE ALWAYSse déclencheront quel que soit le mode de réplication actuel.
J'insiste sur le fait que la configuration ne s'applique pas à tout à la fois, car ALTER TABLE, mais seulement à notre connexion spéciale séparée. Total pour qu'aucune application ne déclenche le feu:

SET session_replication_role = replica; --  
UPDATE ...;
SET session_replication_role = DEFAULT; --    

Condition à l'intérieur de la gâchette


Mais l'option ci-dessus fonctionne pour tous les déclencheurs à la fois (ou vous devez «déclencher» à l'avance des déclencheurs que vous ne voulez pas désactiver). Et si nous devons "désactiver" un déclencheur spécifique ?

La variable de session «utilisateur» nous aidera avec ceci :
Les noms des paramètres d'extension sont écrits comme suit: nom de l'extension, point, puis le nom du paramètre lui-même, comme les noms complets des objets dans SQL. Par exemple: plpgsql.variable_conflict.
Étant donné que les paramètres non système peuvent être définis dans des processus qui ne chargent pas le module d'extension correspondant, PostgreSQL accepte les valeurs de tous les noms à deux composants .
Tout d'abord, modifiez le déclencheur, quelque chose comme ceci:
BEGIN
    --     
    IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
        IF TG_OP IN ('INSERT', 'UPDATE') THEN
            RETURN NEW;
        ELSE
            RETURN OLD;
        END IF;
    END IF;
...

Soit dit en passant, cela peut être fait "de manière rentable", sans verrouillage, grâce CREATE OR REPLACEà une fonction de déclenchement. Et puis dans la connexion spéciale, nous armons "notre" variable:

SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; --    

Connaissez-vous d'autres moyens? Partagez dans les commentaires.

All Articles