Data

10 nov, 2008

Triggers no PostgreSQL

Publicidade

Para o desenvolvimento deste artigo foram utilizadas a versão 8.2.1 do servidor PostgreSQL e a ferramenta de administração e desenvolvimento, pgAdmin III, para a execução de scripts. Essa ferramenta é disponibilizada junto com o pacote de instalação do servidor PostgreSQL, mas também pode ser obtida no seguinte endereço: http://www.pgadmin.org/download/.

Introdução

Triggers são procedimentos armazenados que são acionados por algum evento e em determinado momento. Na maioria dos bancos de dados estes eventos podem ser inserções (INSERT), atualizações (UPDATE) e exclusões (DELETE), e os momentos podem ser dois: antes da execução do evento (BEFORE) ou depois (AFTER). E isso também vale para o PostgreSQL.

Um diferencial das triggers deste banco de dados para outros é que no PostgreSQL as triggers são sempre associadas a funções de triggers (veremos isso mais adiante) e, nos demais, criamos o corpo da trigger na própria declaração desta.

Tipos de triggers

O PostgreSQL possui dois tipos de triggers: triggers-por-linha e triggers-por-instrução.

A trigger-por-linha é disparada uma vez para cada registro afetado pela instrução que disparou a trigger. Já a trigger-por-instrução é disparada somente uma vez quando a instrução é executada.

Funções de trigger e linguagens procedurais

Funções de triggers são funções que não recebem nenhum parâmetro e retornam o tipo trigger. Essas funções recebem uma estrutura chamada TriggerData, e esta é passada internamente para a função pelo PostgreSQL.

O PostgreSQL disponibiliza duas variáveis importantes para serem usadas em conjunto com as triggers-por-linha: NEW e OLD.

A variável NEW, no caso do INSERT, armazena o registro que está sendo inserido. No caso do UPDATE, armazena a nova versão do registro depois da atualização.

A variável OLD, no caso do DELETE, armazena o registro que está sendo excluído. No caso do UPDATE, armazena a antiga versão do registro depois da atualização.

As funções de triggers devem ser escritas em C (linguagem C) ou alguma linguagem procedural disponível no banco de dados. Essas liguagens podem ser várias, como Ruby, Perl, Python, entre outras. Atualmete existem quatro linguagens procedurais disponíveis na distribuição padrão do PostgreSQL: PL/pgSQL, PL/Tcl, PL/Perl e PL/Python. Mas é possível que o usuário defina outras linguagens. Para instalar novas linguagens no PostgreSQL, consulte a documentação oficial do PostgreSQL, que pode ser obtida no seguinte endereço: http://pgdocptbr.sourceforge.net/.

Cada linguagem, que suporta triggers, possui o seu próprio método para tornar os dados de entrada da trigger disponíveis para a função. Estes dados de entrada incluem o tipo de evento da trigger, assim como as opções informadas na criação da trigger. Para uma trigger no nível de linha, os dados de entrada também incluem as linhas NEW para as triggers de INSERT e UPDATE, e a linha OLD para os triggers de UPDATE e DELETE.

Ainda nesse artigo veremos como criar funções de triggers utilizando linguagens procedurais.

Criando uma trigger

A sintaxe para a criação de uma trigger é apresentada abaixo:

CREATE TRIGGER nome { BEFORE | AFTER } { evento [ OR ... ] } 
ON  tabela [ FOR [ EACH ] { ROW | STATEMENT } ] 
    EXECUTE PROCEDURE 
nome_da_função ()

Argumentos:

  • nome é o nome da trigger.
  • before | after determina se a função será chamada antes ou depois do evento.
  • evento indica em que momento a trigger será disparada. A trigger pode ser dispara antes ou depois de um evento de DELETE, UPDATE ou INSERT.

  • tabela indica em qual tabela a trigger estará associada.
  • row | statement especifica se a trigger deve ser disparada uma vez para cada linha afetada pelo evento ou apenas uma vez por comando SQL. Se não for especificado nenhum dos dois, o padrão é FOR EACH STATEMENT.
  • nome_da_função especifica a função de trigger.

Um exemplo de criação de uma trigger é apresentado abaixo:

CREATE TRIGGER  "u_tg_validaCpf " BEFORE INSERT 
ON CLIENTE FOR EACH ROW 
    EXECUTE PROCEDURE  u_fn_validaCpf();

Criando triggers functions (funções de trigger)

Vamos agora aprender a criar funções de trigger. Uma função de trigger deve ser codificada em uma linguagem procedural disponível no banco de dados ou em linguagem C.

Para nossos exemplos, vamos criar um banco de dados, um esquema (schema) e duas tabelas para demonstrar o uso dessas funções.

Para criar esses objetos, execute os scripts abaixo:

/* Criação do banco de dados */
CREATE DATABASE bd_triggers
  WITH ENCODING='WIN1252'
       OWNER="SYSDBA"
       TABLESPACE=pg_default;

/* Criação do esquema  que conterá as tabelas e triggers */	   
CREATE SCHEMA sc_triggers;

/* Criação das tabelas onde serão usadas as triggers */
CREATE TABLE sc_triggers.tb_a (
	cod serial,
	valor varchar(20),
	CONSTRAINT pk_tb_a_cod PRIMARY KEY (cod)
);

CREATE TABLE sc_triggers.tb_b (
	cod serial,
	valor varchar(20),
	numero int,
	CONSTRAINT pk_tb_b_cod PRIMARY KEY (cod),
	CONSTRAINT fk_tb_b_numero FOREIGN KEY (numero)
		REFERENCES sc_triggers.tb_a (cod)
);

Vamos agora criar uma função de trigger simples, que irá apenas inserir um novo registro na tabela “tb_b” quando inserirmos algo na tabela “tb_a”. Observe que a tabela “tb_b” possui uma chave estrangeira da tabela “tb_a”, logo, para inserirmos algo nessa tabela temos que ter o registro já gravado na tabela “tb_a”. Para criarmos a função, antes temos que definir em qual linguagem procedural esta função será codifificada. Vamos utilizar a linguagem PL/pgSQL para criar nossa função de trigger. Antes de criar a função temos que verificar se esta linguagem está disponível no banco de dados. Para isso basta executar o seguinte comando:

SELECT * FROM PG_LANGUAGE;

Temos o seguinte resultado:

A tabela de sistema PG_LANGUAGE registra as linguagens disponíveis no banco de dados. Repare que a linguagem plpgsql (PL/pgSQL) já está instalada no banco de dados.

Agora vamos criar a função. Execute os comandos abaixo:

CREATE OR REPLACE FUNCTION sc_triggers.fn_insert_tb_b() 
RETURNS trigger LANGUAGE plpgsql
AS
    'begin 
insert into sc_triggers.tb_b (sc_triggers.tb_b.valor, sc_triggers.tb_b.numero)
    values (new.valor, new.cod);
        	return new;
    end; ';

Criamos a função “fn_insert_tb_b” no esquema “sc_triggers”. Observe que essa função não recebe parâmetros e retorna o tipo trigger. A linguagem que será utilizada para escrever a função é especificada logo após a palavra “LANGUAGE”. Depois, criamos o código da função entre aspas, após a palavra “AS”. Observe que nossa função faz apenas um insert na tabela “tb_b” com base na insert que está sendo feito na tabela “tb_a” e retorna a linha NEW. Devemos retornar uma linha para indicar ao PostgreSQL que ele deve continuar realizando a operação.

Certo. Agora vamos criar a trigger, para isso execute o script abaixo:

CREATE TRIGGER u_tg_insert_tb_b AFTER INSERT
ON sc_triggers.tb_a FOR EACH ROW    
      EXECUTE PROCEDURE sc_triggers.fn_insert_tb_b();

Nossa trigger é uma trigger AFTER INSERT e está associada a tabela “tb_a” do esquema “sc_triggers”, ou seja, será disparada logo após um insert ter sido realizado na tabela “tb_a”.

Depois de criarmos nossa função e trigger, vamos testá-las. Para fazer isso execute o seguinte insert:

INSERT INTO sc_triggers.tb_a (VALOR )
  VALUES ( 'abc' )

Agora se fizermos um select na tabela “tb_b” obteremos o seguinte resultado:

Observe que inserimos o registro na tabela “tb_a” o que disparou nossa trigger que inseriu um registro na tabela “tb_b”. Vamos agora fazer algo diferente. Execute o seguinte script:

INSERT INTO sc_triggers.tb_a (VALOR )
SELECT 'def' AS VALOR
UNION
SELECT 'ghi' AS VALOR

Esse script vai inserir dois registros na tabela “tb_a” que irá disparar a trigger a inserir também dois registros na tabela “tb_b”.

Depois fazemos um select na tabela “tb_b”. O resultado é mostrado abaixo:

Nossa trigger está funcionando corretamente.

Ordem de execução de triggers

Podemos ter mais de uma trigger associada ao mesmo evento e momento, neste caso a ordem de execução das triggers é definida pela ordem alfabética de seus nomes.

Triggers recursivas

Se uma função de trigger executar comandos SQL, estes comandos podem disparar triggers novamente. Isto é

conhecido como cascatear triggers. Não existe limitação direta do número de níveis de cascateamento. É possível que o cascateamento cause chamadas recursivas da mesma trigger; por exemplo, um trigger para

INSERT pode executar um comando que insere uma linha adicional na mesma tabela, fazendo com que o trigger para INSERT seja disparado novamente. É responsabilidade do programador evitar recursões infinitas nestes casos.

Alterando uma trigger

A sintaxe do comando para alterar uma trigger é apresentada abaixo:

ALTER TRIGGER nome ON tabela RENAME TO novo_nome

Argumentos:

  • nome é nome do gatilho existente a ser alterado.
  • tabela é o nome da tabela onde o gatilho atua.
  • novo_nome é o novo nome do gatilho.

Exemplo :

ALTER TRIGGER u_tg_insert_tb_est_triggers ON  SC_TRIGGERS.TB_TESTE_TRIGGERS  RENAME TO usr_tg_insert_tb_est_triggers;

Excluindo uma trigger

Para excluir uma trigger basta executar o comando abaixo:

DROP TRIGGER nome ON tabela [ CASCADE | RESTRICT ] 

Argumentos:

  • nome é o nome do gatilho a ser removido.
  • tabela é o nome da tabela para a qual o gatilho está definido.
  • [ CASCADE | RESTRICT ] indica se ao remover a trigger vamos remover também todos os objetos que dependem dela (CASCADE) ou recusaremos sua exclusão (RESTRICT).

Exemplo:

DROP TRIGGER u_tg_insert_tb_est_triggers ON SC_TRIGGERS.TB_TESTE_TRIGGERS;

Habilitando/Desabilitando triggers

Para desabilitar uma trigger execute o comando abaixo:

ALTER TABLE  nome_tabela
DISABLE TRIGGER nome_trigger

Para desabilitar todas as triggers da tabela, execute o seguinte comando:

ALTER TABLE  nome_tabela
DISABLE TRIGGER ALL

Exemplo:

ALTER TABLE SC_TRIGGERS.TB_TESTE_TRIGGERS
DISABLE TRIGGER U_TG_INSERT_TB_EST_TRIGGERS

Para habilitar uma trigger basta alterar o parâmetro DISABLE para ENABLE, observe abaixo:

ALTER TABLE SC_TRIGGERS.TB_TESTE_TRIGGERS
ENABLE TRIGGER U_TG_INSERT_TB_EST_TRIGGERS

Agora para habilitar todas as triggers da tabela:

ALTER TABLE SC_TRIGGERS.TB_TESTE_TRIGGERS
ENABLE TRIGGER U_TG_INSERT_TB_EST_TRIGGERS

Obtendo informações sobre as triggers do banco de dados

Podemos obter várias informações sobre as triggers de nosso banco de dados. Essas informações estão disponíveis na view triggers do schema information_schema presente em qualquer banco de dados do PostgreSQL. Essa view possui algumas colunas importantes. Veja a tabela abaixo:

Exemplo:

SELECT * FROM INFORMATION_SCHEMA.TRIGGERS

Podemos também obter essas informações diretamente da tabela de sistema “pg_trigger” do schema “pg_catalog”.

Exemplo:

SELECT * FROM PG_CATALOG.PG_TRIGGER

Finalizo assim este artigo. Abraços

Referências: