Antipadrões do PostgreSQL: alterando dados ignorando um gatilho

Mais cedo ou mais tarde, muitos enfrentam a necessidade de consertar massivamente algo nas entradas da tabela. Eu já disse como fazê-lo melhor e como - é melhor não fazê-lo. Hoje vou falar sobre o segundo aspecto da atualização em massa - acionar gatilhos .

Por exemplo, em uma tabela na qual você precisa consertar algo, um gatilho maligno trava ON UPDATE, transferindo todas as alterações para alguns agregados. E você precisa atualizar tudo (inicialize um novo campo, por exemplo) com tanto cuidado para que essas unidades não sejam afetadas.

Vamos desligar os gatilhos!


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

Na verdade, é tudo - tudo já está pendurado .

Porque ALTER TABLEimpõe um bloqueio AccessExclusive , sob o qual ninguém executando em paralelo, mesmo um simples SELECT, pode ler qualquer coisa da tabela. Ou seja, até que essa transação seja concluída, todos que quiserem “apenas ler” aguardarão. E lembramos que UPDATEfazemos o-o-olgi ...

Vamos desligá-lo rapidamente e ligá-lo rapidamente!


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

UPDATE ...;

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

Aqui a situação é melhor, o tempo de espera é muito menor. Mas apenas dois problemas estragam toda a beleza:

  • ALTER TABLE ele espera por todas as outras operações na mesa, incluindo longas SELECT
  • Enquanto o gatilho estiver desligado, qualquer alteração na tabela passará , nem mesmo a nossa. E bem, ele não entra nas unidades, embora deva. Problema!

Gerenciamento de variável de sessão


Portanto, na versão anterior, deparamos com um ponto crucial - precisamos ensinar de alguma forma o gatilho para distinguir “nossas” alterações na tabela de “não nossas”. Pule "o nosso" como está, mas "não o nosso" - gatilho. Você pode usar variáveis ​​de sessão para isso .

session_replication_role


Leia o manual :
A variável acionadora também é afetada pela variável de configuração session_replication_role . Os gatilhos ativados sem instruções adicionais (padrão) serão acionados quando a função de replicação for "origem" (padrão) ou "local". Os acionadores ativados por uma indicação ENABLE REPLICAserão acionados apenas se o modo de sessão atual for "réplica", e os acionadores ativados por uma indicação ENABLE ALWAYSserão acionados independentemente do modo de replicação atual.
Enfatizo que a configuração não se aplica a todos de uma vez ALTER TABLE, mas apenas à nossa conexão especial separada. Total para que nenhum aplicativo inicie o disparo:

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 só vez (ou você precisa "acionar" gatilhos com antecedência que não deseja desativar). E se precisarmos "desligar" um gatilho específico ?

A variável de sessão "user" nos ajudará com isso :
Os nomes dos parâmetros de extensão são escritos da seguinte forma: nome da extensão, período e, em seguida, o próprio nome do parâmetro, como os nomes completos dos objetos no SQL. Por exemplo: plpgsql.variable_conflict.
Como os parâmetros que não são do sistema podem ser definidos em processos que não carregam o módulo de extensão correspondente, o PostgreSQL aceita valores para qualquer nome com dois componentes .
Primeiro, modifique o gatilho, algo como isto:
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 "lucrativamente", sem bloqueios, através CREATE OR REPLACEde uma função de gatilho. E então, na conexão especial, tornamos a variável "nossa":

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

Você conhece outras maneiras? Compartilhe nos comentários.

All Articles