Manipulando Triggers no PostgreSQL

Olá novamente caros leitores!

Primeiramente me desculpem pela demora para liberar uma nova postagem sobre o Postgres, é que as últimas semanas foram muito movimentadas, realizei minha entrevista na UFSCar para o ingresso no programa de mestrado do Departamento de Computação e ainda na semana retrasada recebi a notícia da minha aprovação, então imaginem o tamanho da felicidade e a correria com as festas de fim de ano.

Bom, mas vamos ao que interessa, hoje irei falar sobre as triggers, ou funções de gatilho, como você queria chamar. Uma das coisas mais interessantes da implementação das funções no Postgres é que você pode escolher qual linguagem vai usar para programar sua trigger! Isso mesmo, então antes de mais nada, eu separei abaixo a lista de algumas linguagens, e o link para mais informações sobre elas, veja:

Agora que você já sabe dessa maravilha do Postgres irei dizer que aqui, iremos exemplificar as triggers utilizando a PL/pgSQL. Mas antes da prática no código fonte, iremos trabalhar um pouco sobre a definição e funcionamento das triggers no PostgreSQL.

Você deve saber que os nossos gatilhos funcionam baseados em eventos, dois: before (antes de alguma coisa) e after (depois de alguma coisa). Explicando: você pode determinar que uma trigger dispare após (after) a inserção em determinada tabela do banco ou antes (before) da tal inserção, por exemplo.

É muito importante que você dê uma olhada abaixo na lista de variáveis que podem ser utilizadas dentro da estrutura da função da sua trigger, vale a pena dar uma lida, no entanto existem duas que a leitura é obrigatoria, que são a new e a old. Elas são usadas da seguinte maneira:

  • NEW: No caso da trigger ser disparada por um INSERT a variável NEW irá abrigar os valores a ser inseridos, e caso seja um evento do tipo UPDATE, NEW irá conter a nova versão dos valores a serem atualizados (veja o exemplo no código abaixo).
  • OLD: Esta variável é utilizada no caso do evento DELETE armazena os dados que estão sendo excluídos, e no caso do UPDATE a versão antiga dos dados.

O acesso dos dados se dá em ambas as variáveis dessa maneira: NEW.COD_CLIENTE, por exmplo. Abaixo você verá o exemplo de uma trigger.

Outras variáveis especiais que você pode usar:

  • TG_NAME: Contém o nome do trigger que foi disparado.
  • TG_WHEN: Contem quando o evento foi disparado, sendo BEFORE ou AFTER.
  • TG_LEVEL: Diz em qual ponto da trigger.
  • TG_OP: Tipo de operção que está sendo executada: INSERT, UPDATE ou DELETE.
  • TG_RELID: Contém a ID do objeto que está disparando o gatilho.
  • TG_RELNAME: Nome da tabela que disparou o gatilho (obsoleto).
  • TG_TABLE_NAME: Nome da tabela que disparou o gatilho.
  • TG_NAME_SCHEMA: Nome do schema onde está a tabela que disparou a trigger.
  • TG_NARGS: Número de argumentos fornecidos para a Stored Function do trigger.
  • TG_ARGV[]: Os argumentos que foram fornecidos.

Perguntas (quase) nunca feitas sobre triggers:

  • Sabe-se que pode atrelar mais de uma trigger por tabela, então qual a ordem de execução das triggers?
    R: A ordem de execução é alfabética.
  • Triggers podem ser recursivas?
    R: Sim! Os gatilhos podem executar comandos SQL, e os mesmos podem disparar outras triggers. Este cenário é conhecido como cascateamento de triggers, assim pode ser que a trigger passe a se “auto disparar”, ou seja, chamadas recursivas da própria trigger, fique atento a recursões infinitas!

Triggers na Prática

Agora vamos a parte pática irei realizar um único exemplo, neste link você e encontrar mais exemplos de triggers. No nosso, considere as duas tabelas abaixo:

CREATE TABLE funcionarios (
	ID_FUNCIONARIO SERIAL,
	NOME VARCHAR(255),
	IDADE INTEGER,
	FUNCAO VARCHAR(150),
	PRIMARY KEY(ID_FUNCIONARIO)
);

CREATE TABLE FUNCIONARIOS_LOG (
	COD_ALTERACAO SERIAL,
	USUARIO VARCHAR(150) NOT NULL,
	TIPO_ACAO VARCHAR(25) NOT NULL,
	DATA_ALTERACAO TIMESTAMP NOT NULL,
	PRIMARY KEY (COD_ALTERACAO)
);

Repare que na construção da tabela funcionarios eu não coloquei nenhuma regra de validação, como por exemplo not null, foi de propósito! Nossa trigger irá realizar a (simples) validação da entrada de dados na tabela e em seguida guardar que tipo de operação foi feita, por quem e a data na tabela funcionarios_log.

Agora a construção da stored function que será “chamada” pela trigger e sua agregação como trigger:

CREATE FUNCTION valida_dados_funcionario() RETURNS TRIGGER AS $valida_dados_funcionario$
BEGIN
	IF NEW.NOME IS NULL THEN
		RAISE EXCEPTION 'Por Favor, digite o nome do funcionario!';
	END IF;

	IF NEW.IDADE IS NULL THEN
		RAISE EXCEPTION 'Por favor, informe a idade!';
	END IF;

	IF NEW.IDADE < 0 THEN
		RAISE EXCEPTION 'Desculpe, o funcionario não pode ter % anos', NEW.IDADE;
	END IF;

	IF NEW.FUNCAO IS NULL THEN
		RAISE EXCEPTION 'Por favor, informe qual a função do funcinário!';
	END IF;

	-- Aqui iremos gravar no log o tipo de ação que foi realizada
	INSERT INTO FUNCIONARIOS_LOG (USUARIO, TIPO_ACAO, DATA_ALTERACAO) VALUES (CURRENT_USER, TG_OP, CURRENT_TIMESTAMP);

	RETURN NEW;
END;
$valida_dados_funcionario$
LANGUAGE plpgsql;

Note que realizei as devidas validações utilizando o operador NEW, porque agora iremos atrelar a trigger a tabela dizendo que ela será executada durante um insert ou update. Observe também que o declaramos um return “trigger” e especificamos os delimitadores $$, você pode encontrar mais informações sobre a estruturação de procedures de triggers na documentação do PostgreSQL, e por fim note que especificamos qual linguagem é utilizada para a construção da trigger, no caso plpgsql.

Veja abaixo como definimos a trigger, chamando a procedure que construímos anteriormente:

CREATE TRIGGER validacao_insert BEFORE INSERT OR UPDATE ON funcionarios
FOR EACH ROW EXECUTE PROCEDURE valida_dados_funcionario();

Pronto! Agora sua trigger já está funcionando nos eventos INSERT e UPDATE na tabela funcionario, faça os testes!

Agora para ilustrar ainda mais nosso exemplo irei alterar o nome da trigger, veja:

ALTER TRIGGER validacao_insert ON funcionarios RENAME TO a_validacao_insert

Para desabilitar (para habilitar troque o DISABLE por ENABLE) uma determinada trigger:

ALTER TABLE  funcionarios DISABLE TRIGGER a_validacao_insert

Ou para desabilitar todas as triggers de uma tabela:

ALTER TABLE  funcionarios DISABLE TRIGGER ALL

Agora, se eu desejar remover a trigger:

DROP TRIGGER a_validacao_insert ON funcionarios

Repare que não postei a imagem dos resultados para induzir você a testar o código! Faça os scripts de insert e update e veja as saídas produzidas pelo SGBD, acredito que você irá se divertir muito com os resultados!

Se você quiser saber mais sobre os triggers e sobre o PostgreSQL eu recomendo um livro (se você também for chegado a papel) que utilizei na minha monografia, intitulado PostgreSQL: Guia do Desenvolvedor, de André Milani.

Como você que leu (conseguiu chegar até aqui) triggers são um assunto muito extenso para se tratar aqui no blog, recomendo que você leia as referências e pesquisa mais sobre o assunto, é um tema muito interessante e útil para o dia a dia!

Referências:

Abraços,

Tiago.

 

Add a Comment

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *