Tarde o temprano, muchos se enfrentan a la necesidad de arreglar masivamente algo en las entradas de la tabla. Ya dije cómo hacerlo mejor y cómo, es mejor no hacerlo. Hoy hablaré sobre el segundo aspecto de la actualización masiva: los desencadenantes .Por ejemplo, en una mesa en la que necesita arreglar algo, se cuelga un gatillo malvado que ON UPDATE
transfiere todos los cambios a algunos agregados. Y debe actualizar todo (inicializar un nuevo campo, por ejemplo) con tanto cuidado que estas unidades no se vean afectadas.¡Solo apaguemos los disparadores!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
UPDATE ...;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
En realidad, eso es todo: todo ya está colgando .Debido a que ALTER TABLE
impone un bloqueo AccessExclusive , bajo el cual nadie que se ejecuta en paralelo, incluso uno simple SELECT
, puede leer cualquier cosa de la tabla. Es decir, hasta que se complete esta transacción, todos los que quieran "simplemente leer" esperarán. Y recordamos que UPDATE
hacemos-o-olgi ...¡Apagémoslo rápidamente, luego enciéndalo rápidamente!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;
UPDATE ...;
BEGIN;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Aquí la situación es mejor, el tiempo de espera es mucho más corto. Pero solo dos problemas estropean toda la belleza:ALTER TABLE
él espera todas las demás operaciones sobre la mesa, incluso largas SELECT
- Mientras el gatillo esté apagado, cualquier cambio en la tabla pasará volando , ni siquiera el nuestro. Y bueno, no entrará en las unidades, aunque debería. ¡Problema!
Gestión de variables de sesión
Entonces, en la versión anterior, nos topamos con un punto crucial: necesitamos enseñar de alguna manera el disparador para distinguir "nuestros" cambios en la tabla de "no los nuestros". Omita "el nuestro" tal como está, pero "no nuestro" - disparador. Puede usar variables de sesión para esto .session_replication_role
Lee el manual :El mecanismo de activación también se ve afectado por la variable de configuración session_replication_role . Los activadores que están habilitados sin instrucciones adicionales (predeterminado) se activarán cuando el rol de replicación sea "origen" (predeterminado) o "local". Los disparadores habilitados por una indicación ENABLE REPLICA
se dispararán solo si el modo de sesión actual es "réplica", y los disparadores habilitados por una indicación ENABLE ALWAYS
se dispararán independientemente del modo de replicación actual.
Insisto en que la configuración no se aplica a todo a la vez ALTER TABLE
, sino solo a nuestra conexión especial separada. Total para que ninguna aplicación active el fuego:SET session_replication_role = replica;
UPDATE ...;
SET session_replication_role = DEFAULT;
Condición dentro del gatillo
Pero la opción anterior funciona para todos los desencadenantes a la vez (o necesita "desencadenar" desencadenantes por adelantado que no desea deshabilitar). ¿Y si necesitamos "apagar" un disparador específico ?La variable de sesión "usuario" nos ayudará con esto :Los nombres de los parámetros de extensión se escriben de la siguiente manera: nombre de la extensión, punto y luego el nombre del parámetro en sí, como los nombres completos de los objetos en SQL. Por ejemplo: plpgsql.variable_conflict.
Dado que los parámetros que no son del sistema se pueden establecer en procesos que no cargan el módulo de extensión correspondiente, PostgreSQL acepta valores para cualquier nombre con dos componentes .
Primero, modifique el disparador, algo como esto: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 cierto, esto se puede hacer de manera "rentable", sin bloqueos, a través CREATE OR REPLACE
de una función de disparo. Y luego, en la conexión especial, seleccionamos "nuestra" variable:
SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = '';
¿Conoces otras formas? Comparte en los comentarios.