PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Dentro de uma função de gatilho, como obter quais campos estão sendo atualizados


Se uma "fonte" não "enviar um identificador", a coluna não será alterada. Então você não pode detectar se o UPDATE atual foi feito pela mesma fonte que a última ou por uma fonte que não alterou em nada a coluna. Em outras palavras:isso não funciona corretamente.

Se a "fonte" for identificável por qualquer função de informação de sessão, você pode trabalhar com isso. Como:
NEW.column = session_user;

Incondicionalmente para cada atualização.

Solução geral


Eu encontrei uma maneira de resolver o problema original. A coluna será definida como um valor padrão em qualquer update onde a coluna está não atualizada (não no SET lista de UPDATE ).

O elemento-chave é um acionador por coluna introduzido no PostgreSQL 9.0 - um gatilho específico de coluna usando o UPDATE OF column_name cláusula.

O gatilho só será acionado se pelo menos uma das colunas listadas for mencionada como destino do UPDATE comando.

Essa é a única maneira simples que encontrei para distinguir se uma coluna foi atualizada com um novo valor idêntico ao antigo ou não atualizado.

Um poderia também analisa o texto retornado por current_query() . Mas isso parece complicado e não confiável.

Funções de gatilho


Eu assumo uma coluna col definido NOT NULL .

Etapa 1: Definir col para NULL se inalterado:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step1()
  RETURNS trigger AS
$func$
BEGIN
   IF OLD.col = NEW.col THEN
      NEW.col := NULL;      -- "impossible" value
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Etapa 2: Reverter para o valor antigo. O acionador só será acionado se o valor for realmente atualizado (ver abaixo):
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step2()
  RETURNS trigger AS
$func$
BEGIN
   IF NEW.col IS NULL THEN
      NEW.col := OLD.col;
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Etapa 3: Agora podemos identificar a atualização que falta e definir um valor padrão:
CREATE OR REPLACE FUNCTION trg_tbl_upbef_step3()
  RETURNS trigger AS
$func$
BEGIN
   IF NEW.col IS NULL THEN
      NEW.col := 'default value';
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Acionadores


O acionador da Etapa 2 é disparado por coluna!
CREATE TRIGGER upbef_step1
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step1();

CREATE TRIGGER upbef_step2
  BEFORE UPDATE OF col ON tbl                -- key element!
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step2();

CREATE TRIGGER upbef_step3
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step3();

Nomes dos acionadores são relevantes, porque são disparados em ordem alfabética (todos sendo BEFORE UPDATE )!

O procedimento pode ser simplificado com algo como "gatilhos por não-coluna" ou qualquer outra maneira de verificar a lista de destino de um UPDATE em um gatilho. Mas não vejo alça para isso.

Se col pode ser NULL , use qualquer outro valor intermediário "impossível" e verifique se há NULL adicionalmente na função de disparo 1:
IF OLD.col IS NOT DISTINCT FROM NEW.col THEN
    NEW.col := '#impossible_value#';
END IF;

Adapte o resto de acordo.