Antipatróns de PostgreSQL: cambia os datos sen pasar un disparador
Tarde ou cedo, moitos enfróntanse á necesidade de arranxar masivamente algo nos rexistros da táboa. Xa o teño dime como facelo mellor, e como - é mellor non facelo. Hoxe falarei do segundo aspecto da actualización masiva: sobre os disparadores.
Por exemplo, nunha mesa na que tes que arranxar algo, colga un gatillo malvado ON UPDATE, transferindo todos os cambios a algúns agregados. E cómpre actualizar todo (iniciar un campo novo, por exemplo) con tanto coidado que estes agregados non se vexan afectados.
Imos desactivar os disparadores!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
UPDATE ...; -- тут долго-долго
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
En realidade, iso é todo - todo está colgado.
Porque ALTER TABLE impón Acceso Exclusivo- un peche baixo o que ninguén corre en paralelo, nin sequera un sinxelo SELECT, non poderá ler nada da táboa. É dicir, ata que remate esta transacción, todos os que queiran incluso "só ler" esperarán. E recordamos iso UPDATE temos unha longa...
Apaguemolo rapidamente e despois acendémolo rapidamente!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;
UPDATE ...;
BEGIN;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Aquí a situación xa é mellor, o tempo de espera é moito menor. Pero só dous problemas estragan toda a beleza:
ALTER TABLE agarda por todas as demais operacións sobre a mesa, incluídas as longas SELECT
Mentres o gatillo está apagado, "voar" calquera cambio na mesa, nin sequera a nosa. E non entrará nos agregados, aínda que debería. Problema!
Xestión de variables de sesión
Entón, na versión anterior, tropezámonos cun punto fundamental: debemos ensinar dalgunha maneira o disparador a distinguir os "nosos" cambios na táboa de "non os nosos". "Os nosos" sáltanse tal e como están, pero en "non o noso" desencadean. Para iso podes usar variables de sesión.
O mecanismo de activación tamén se ve afectado pola variable de configuración role_replication_sesión. Activados sen instrucións adicionais (predeterminado), os disparadores activaranse cando a función de replicación sexa "orixe" (predeterminada) ou "local". Activadores habilitados ao especificar ENABLE REPLICA, funcionará só se modo de sesión actual - "réplica" e activadores habilitados ao especificar ENABLE ALWAYS, funcionará independentemente do modo de replicación actual.
Destacarei especialmente que a configuración non se aplica a todos á vez, xa que ALTER TABLE, pero só coa nosa conexión especial separada. En total, para que ningunha aplicación desencadee o traballo:
SET session_replication_role = replica; -- выключили триггеры
UPDATE ...;
SET session_replication_role = DEFAULT; -- вернули в исходное состояние
Condición dentro do gatillo
Pero a opción anterior funciona para todos os disparadores á vez (ou necesitas "alternar" os disparadores con antelación que non queres desactivar). E se necesitamos "desactivar" un disparador específico?
Os nomes dos parámetros de extensión escríbense do seguinte xeito: o nome da extensión seguido dun punto e despois o propio nome do parámetro, de xeito similar aos nomes completos dos obxectos en SQL. Por exemplo: plpgsql.variable_conflict.
Debido a que as opcións fóra do sistema pódense establecer en procesos que non cargan o módulo de extensión adecuado, PostgreSQL acepta valores para calquera nome con dous compoñentes.
Primeiro, finalizamos o disparador, algo así:
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;
...
Por certo, isto pódese facer "con ánimo de lucro", sen bloquear, a través CREATE OR REPLACE para a función de disparo. E despois, na conexión especial, identificamos a "nosa" variable:
SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- вернули в исходное состояние