Antipadrões do PostgreSQL: alterar dados ignorando um gatilho
Mais cedo ou mais tarde, muitos se deparam com a necessidade de consertar massivamente algo nos registros da tabela. eu já diga-me como fazer melhor, e como - é melhor não fazer isso. Hoje falarei sobre o segundo aspecto da atualização em massa - sobre gatilhos.
Por exemplo, em uma mesa em que você precisa consertar algo, um gatilho maligno trava ON UPDATE, transferindo todas as alterações para alguns agregados. E você precisa atualizar tudo (inicializar um novo campo, por exemplo) com muito cuidado para que esses agregados não sejam afetados.
Vamos apenas desabilitar os gatilhos!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
UPDATE ...; -- тут долго-долго
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Na verdade, isso é tudo - tudo está pendurado.
Porque ALTER TABLE impõe Acesso Exclusivo- um bloqueio sob o qual ninguém está correndo em paralelo, mesmo um simples SELECT, não poderá ler nada da tabela. Ou seja, até que essa transação termine, todos que quiserem "apenas ler" vão esperar. E nós lembramos disso UPDATE temos um longo...
Vamos desligá-lo rapidamente e, em seguida, ligá-lo rapidamente!
BEGIN;
ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;
UPDATE ...;
BEGIN;
ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;
Aqui a situação já é melhor, o tempo de espera é bem menor. Mas apenas dois problemas estragam toda a beleza:
ALTER TABLE ele próprio espera por todas as outras operações na mesa, incluindo as longas SELECT
Enquanto o gatilho estiver desligado, "voar por" qualquer mudança na mesa, nem mesmo a nossa. E não entrará nos agregados, embora devesse. Dificuldade!
Gerenciando variáveis de sessão
Então, na versão anterior, nos deparamos com um ponto fundamental - precisamos de alguma forma ensinar o gatilho a distinguir “nossas” mudanças na tabela de “não nossas”. “Ours” são ignorados como estão, mas em “not ours” eles são acionados. Para isso você pode usar variáveis de sessão.
O mecanismo de gatilho também é afetado pela variável de configuração session_replication_role. Habilitado sem instruções adicionais (padrão), os gatilhos serão acionados quando a função de replicação for "origem" (padrão) ou "local". Gatilhos habilitados especificando ENABLE REPLICA, só funcionará se modo de sessão atual - "réplica" e gatilhos habilitados especificando ENABLE ALWAYS, funcionará independentemente do modo de replicação atual.
Vou enfatizar especialmente que a configuração não se aplica a todos de uma vez, pois ALTER TABLE, mas apenas para nossa conexão especial separada. No total, para que nenhum gatilho de aplicativo funcione:
SET session_replication_role = replica; -- выключили триггеры
UPDATE ...;
SET session_replication_role = DEFAULT; -- вернули в исходное состояние
Condição dentro do gatilho
Mas a opção acima funciona para todos os gatilhos de uma vez (ou você precisa "alternar" os gatilhos com antecedência que não deseja desativar). E se precisarmos "desligar" um gatilho específico?
Os nomes dos parâmetros de extensão são escritos da seguinte forma: o nome da extensão seguido por um ponto e, em seguida, o próprio nome do parâmetro, semelhante aos nomes de objetos completos em SQL. Por exemplo: plpgsql.variable_conflict.
Como as opções fora do sistema podem ser definidas em processos que não carregam o módulo de extensão apropriado, o PostgreSQL aceita valores para quaisquer nomes com dois componentes.
Primeiro, finalizamos o gatilho, algo assim:
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;
...
A propósito, isso pode ser feito "com fins lucrativos", sem bloqueio, por meio de CREATE OR REPLACE para a função de gatilho. E então, na conexão especial, ativamos "nossa" variável:
SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- вернули в исходное состояние
Você conhece outras formas? Compartilhe nos comentários.