Você está na página 1de 67

Functions no PostgreSQL

Tpicos

Conhecendo o PostgreSQL;
Suas principais
caractersticas;
Linguagens procedurais;
Instalao de linguagem
procedural;
Tipos de dados suportados;
Dicas para desenvolver em
PL/pgSQL;

Apstrofos versus Cifro;


Estrutura da linguagem
PL/pgSQL;

Declaraes;

Instrues bsicas;

Estrutura de controle;

Erros e mensagens.

Objetivos

Conhecer algumas funcionalidades avanadas do PostgreSQL;


Entender e criar funes atravs das linguagens SQL e
PL/PgSQL.

Linguagens procedurais

O PostgreSQL permite que as funes definidas pelo


usurio sejam escritas em outras linguagens alm de SQL;

Estas
linguagens so chamadas
linguagens procedurais (PLs);

genericamente

de

No caso de uma funo escrita em uma linguagem


procedural, o servidor de banco de dados no possui
nenhum conhecimento interno sobre como interpretar o
texto do cdigo fonte da funo.

Em vez disso, a tarefa passada para um tratador especial


que conhece os detalhes da linguagem.

Linguagens procedurais

Algumas linguagens procedurais disponveis na distribuio


padro PostgreSQL:

PL/pgSQL

PL/Tcl

PL/Perl

PL/Python

PL/Java

Instalao da linguagem procedural

A linguagem procedural deve ser instalada em cada


banco de dados onde vai ser utilizada;

Para instalar a linguagem PL/pgSQL em todos os bancos de


dados, cria-se a linguagem no template1;

Para instalar a linguagem PL/pgSQL no banco de dados


conectado utilizado:
CREATE LANGUAGE plpgsql;

A linguagem PL/pgSQL

Os objetivos de projeto da linguagem PL/pgSQL foram no


sentido de criar uma linguagem procedural carregvel que
pudesse:

Ser utilizada para criar procedimentos de funes e de gatilhos;

Adicionar estruturas de controle linguagem SQL;

Realizar processamentos complexos;

Herdar

todos

os

tipos

definidos pelo usurio;

Ser fcil de utilizar.

de

dado,

funes

operadores

Tipos de dados suportados nos


argumentos e nos resultados

As funes escritas em PL/pgSQL aceitam como argumento


qualquer tipo de dado suportado pelo servidor, e podem
retornar como resultado qualquer um destes tipos;

Tambm possvel declarar uma funo PL/pgSQL como


retornando RECORD, significando que o resultado um tipo
linha, cujas colunas so determinadas pela especificao no
comando que faz a chamada.

Tipos de dados suportados nos


argumentos e nos resultados

As funes PL/pgSQL tambm podem ser declaradas como


retornando SET (conjunto), ou tabela, de qualquer tipo de
dado;

Por

fim, uma

funo PL/pgSQL pode

ser declarada

como

retornando VOID se no produzir nenhum valor de retorno til.

Desenvolvimento em PL/pgSQL

Uma boa maneira de desenvolver em PL/pgSQL usar a


ferramenta Query do pgAdmin para criar suas funes e
utilizar a sintaxe CREATE OR REPLACE FUNCTION.
Fazendo assim, basta recarregar o arquivo que contm o
cdigo SQL da funo para atualizar a sua definio.
Por exemplo:
CREATE OR REPLACE FUNCTION teste(integer) RETURNS
integer AS '
...
'
LANGUAGE 'plpgsql';

Apstrofos versus Cifro

O cdigo da funo PL/pgSQL especificado no comando


CREATE FUNCTION como uma cadeia de caracteres;

Se a cadeia de caracteres for escrito da maneira usual,


que entre apstrofos ('), ento os apstrofos dentro do
corpo da funo devem ser duplicados;

Duplicar os apstrofos no mnimo entediante, e nos casos


mais

complicados

pode

tornar

cdigo

compreendido, porque pode-se chegar

difcil

de

ser

facilmente a uma

situao onde so necessrios seis ou mais apstrofos


adjacentes.

Apstrofos versus Cifro

Por isso, recomenda-se que o corpo da funo seja escrito


em uma cadeia de caracteres delimitada por cifro em vez
de delimitada por apstrofos;

Na abordagem delimitada por cifro os apstrofos nunca so


duplicados.

Estrutura da Linguagem PL/pgSQL

A linguagem PL/pgSQL estruturada em blocos. O texto


completo da definio da funo deve ser um bloco. Um
bloco definido como:

[ <<rtulo>> ]
[ DECLARE declaraes ]
BEGIN
instrues
END

Estrutura da Linguagem PL/pgSQL

Todas as declaraes e instrues dentro do bloco devem


ser terminadas por ponto-e-vrgula;

O END final que conclui o corpo da funo no requer o pontoe-vrgula;

Todas as palavras chave e identificadores podem


escritos misturando letras maisculas e minsculas;

ser

As letras dos identificadores so convertidas implicitamente


em minsculas, a menos que estejam entre aspas.

Comentrios em PL/pgSQL

Existem dois tipos de comentrios no PL/pgSQL;

O hfen duplo (--) comea um comentrio que se estende


at o final da linha;

O /* comea um bloco de comentrio que se estende at a


prxima ocorrncia de */;

Os blocos de comentrio no podem ser aninhados.

Declaraes

As variveis declaradas na seo de declarao que


precede um bloco so inicializadas com seu valor padro
toda vez que o bloco executado;

As variveis da linguagem PL/pgSQL podem possuir


qualquer tipo de dado da linguagem SQL, como integer,
varchar e char.

Declaraes

id_usuario integer;

quantidade numeric(5);

url varchar;

minha_linha nome_da_tabela%ROWTYPE;

meu_campo nome_da_tabela.nome_da_coluna%TYPE;

uma_linha RECORD;

Declaraes

A sintaxe geral para declarao de variveis :


nome [ CONSTANT
expresso ];

tipo

NOT

NULL

DEFAULT

:=

A clusula DEFAULT, se for fornecida, especifica o valor inicial


atribudo varivel quando o processamento entra no bloco;

Se a clusula DEFAULT no for fornecida, ento a varivel


inicializada com o valor nulo do SQL.

Declaraes

A opo CONSTANT impede que seja atribudo valor a varivel


e, portanto, seu valor permanece constante pela durao do
bloco, se for especificado NOT NULL, uma atribuio de valor
nulo resulta em um erro em tempo de execuo;

Todas as variveis declaradas como NOT NULL devem ter


um valor padro no nulo especificado.
Exemplos:
quantidade integer DEFAULT 32;
url varchar := ''http://meu-site.com'';
id_usuario CONSTANT integer := 10;

Aliases

Os parmetros passados para as funes recebem como


nome os identificadores $1, $2, etc;
Opcionalmente, para melhorar a legibilidade do cdigo,
podem ser declarados aliases para os nomes dos
parmetros.
Para fazer referncia ao valor do parmetro, pode
utilizado tanto o alias quanto o identificador numrico.
CREATE OR REPLACE FUNCTION taxa_de_venda(real)
RETURNS real AS $$
BEGIN
RETURN $1 * 0.06;
END;
$$ LANGUAGE plpgsql;

ser

Aliases

Existem duas maneiras de criar um alis. A forma


preferida fornecer nome ao parmetro no comando CREATE
FUNCTION como, por exemplo:

CREATE OR REPLACE FUNCTION taxa_de_venda(subtotal real)


RETURNS real AS $$
BEGIN
RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Aliases

A outra maneira, declarar explicitamente um alis


utilizando a seguinte sintaxe:

nome ALIAS FOR $n;

Alterando o exemplo anterior para esta sintaxe:


CREATE OR REPLACE FUNCTION taxa_de_venda(real)
RETURNS real AS $$
DECLARE subtotal ALIAS FOR $1;
BEGIN
RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Cpia de tipo

varivel%TYPE
A expresso %TYPE fornece o tipo de dado da varivel ou da
coluna da tabela.
Pode ser utilizada para declarar variveis que armazenam
valores do banco de dados.
Por exemplo, supondo que exista uma coluna chamada
id_usuario na tabela usuarios, para declarar uma varivel
com o mesmo tipo de dado de usuarios.id_usuario deve ser
escrito:
id_usuario usuarios.id_usuario%TYPE;

Cpia de tipo

Utilizando %TYPE no necessrio conhecer o tipo de


dado da estrutura sendo referenciada e, ainda mais
importante, se o tipo de dado do item referenciado mudar no
futuro (por exemplo: o tipo de dado de id_usuario for
mudado de integer para real), no ser necessrio mudar a
definio na funo.

Tipos linha

nome nome_da_tabela%ROWTYPE;
Uma varivel de tipo composto chamada de varivel
linha (ou varivel tipo-linha).
Este tipo de varivel pode armazenar toda uma linha de
resultado de um comando SELECT ou FOR, desde que o
conjunto de colunas do comando corresponda ao tipo
declarado para a varivel.
Os campos individuais do valor linha so acessados
utilizando a notao usual de ponto como, por exemplo,
variavel_linha.campo.

Exemplo
CREATE FUNCTION get_author (integer) RETURNS text AS '
DECLARE
author_id ALIAS FOR $1;
found_author authors%ROWTYPE;
BEGIN
SELECT INTO found_author * FROM authors WHERE id =
author_id;
RETURN found_author.first_name || '' '' ||
found_author.last_name;
END;
' LANGUAGE 'plpgsql';

Tipo registro

nome RECORD;
As variveis registro so semelhantes s variveis tipo-linha,
mas no possuem uma estrutura pr-definida.
Assumem a estrutura da linha para a qual so atribudas
pelo comando SELECT ou FOR.
A subestrutura da varivel registro pode mudar toda vez
que usada em uma atribuio.

Exemplo
CREATE OR REPLACE FUNCTION getNomeEmpregado(id INTEGER)
RETURNS VARCHAR AS
$$
DECLARE
registro RECORD;
BEGIN
SELECT * INTO registro FROM empregado WHERE emp_id = id;
IF NOT FOUND THEN
RAISE EXCEPTION 'empregado % no existente', id;
END IF;
RETURN registro.emp_nome;
END;
$$
LANGUAGE plpgsql;

Atribuies

A atribuio de um valor a uma varivel, ou a um


campo de linha ou de registro, escrita da seguinte maneira:

identificador := expresso;

Exemplos:

id_usuario := 20;

taxa := subtotal * 0.06;

SELECT INTO

O resultado de um comando SELECT que retorna vrias


colunas (mas apenas uma linha) pode ser atribudo a uma
varivel registro ou a uma varivel tipo-linha;

SELECT INTO destino expresses_de_seleo FROM ...;


Onde destino pode ser uma varivel registro, uma varivel
linha, ou uma lista separada por vrgulas de variveis
simples.

SELECT INTO

Deve ser observado que bem diferente da interpretao


normal de SELECT INTO feita pelo PostgreSQL, onde o
destino de INTO uma nova tabela criada.
Se a consulta no retornar nenhuma linha, so atribudos
valores nulos aos destinos.
Se a consulta retornar vrias linhas, a primeira linha
atribuda aos destinos e as demais so desprezadas;
Deve ser observado que a primeira linha no bem definida
a no ser que seja utilizado ORDER BY.

Exemplo
CREATE OR REPLACE FUNCTION getNomeEmpregado(id INTEGER)
RETURNS VARCHAR AS
$$
DECLARE
registro RECORD;
BEGIN
SELECT * INTO registro FROM empregado WHERE emp_id = id;
IF NOT FOUND THEN
RAISE EXCEPTION 'empregado % no existente', id;
END IF;
RETURN registro.emp_nome;
END;
$$
LANGUAGE plpgsql;

FOUND

varivel

imediatamente

especial
aps

FOUND
a

determinar se a atribuio

instruo

pode

ser

SELECT

verificada
INTO

para

foi bem-sucedida, ou seja, foi

retornada pelo menos uma linha pela consulta.

FOUND
CREATE FUNCTION CONSULTAALUNO (vmatricula INT) RETURNS text
AS
$$
DECLARE
registro RECORD;
BEGIN
SELECT INTO registro * FROM ALUNO WHERE matricula =
vmatricula;
IF FOUND THEN
RAISE EXCEPTION 'no existe aluno com a matricula %',
vmatricula;
END IF;
RETURN registro.nome;
END;
$$
LANGUAGE 'PLPGSQL';

FOUND

Para testar se o resultado do registro/linha nulo, pode


ser utilizada a condio IS NULL.
DECLARE
registro_usuario RECORD;
BEGIN
SELECT INTO registro_usuario * FROM usuarios WHERE
id_usuario=3;
IF registro_usuario.pagina_web IS NULL THEN
-- o usuario no informou a pgina na web, retornar
"http://"
RETURN ''http://'';
END IF;
END;

Consulta sem resultado

Algumas vezes se deseja avaliar uma expresso ou


comando e desprezar o resultado (normalmente quando
est sendo chamada uma funo que produz efeitos
colaterais, mas no possui nenhum valor de resultado til).
Para se fazer isto no PL/pgSQL utilizada a instruo
PERFORM:
PERFORM comando;

Consulta sem resultado


CREATE OR REPLACE FUNCTION apagaEmpregado(id INTEGER)
RETURNS void AS
$$
BEGIN
perform * from empregado where emp_id = id;
IF NOT FOUND THEN
RAISE EXCEPTION 'empregado no encontrado ';
ELSE
DELETE FROM empregado WHERE emp_id = id;
RAISE NOTICE 'removido com sucesso';
END IF;
END;
$$
LANGUAGE plpgsql;

No fazer nada

Algumas vezes uma instruo que no faz nada til.


Por exemplo, pode indicar que uma ramificao da cadeia
if/then/else deliberadamente vazia. Para esta finalidade deve
ser utilizada a instruo NULL:
BEGIN
y := x / 0;
EXCEPTION
WHEN division_by_zero THEN NULL;
END;

Estruturas de controle

As estruturas de controle provavelmente so a parte mais


til (e mais importante) da linguagem PL/pgSQL;

Com as estruturas de controle do PL/pgSQL os dados do


PostgreSQL podem ser manipulados de uma forma muito
flexvel e poderosa.

Retorno de funo

Esto disponveis dois comandos que permitem retornar


dados de uma funo:

RETURN

RETURN NEXT.

RETURN

RETURN expresso;
O comando RETURN com uma expresso termina a funo e
retorna o valor da expresso para quem chama.
Qualquer expresso pode ser utilizada para retornar um
tipo escalar. O resultado da expresso automaticamente
convertido no tipo de retorno da funo conforme descrito
nas atribuies.
Para retornar um valor composto (linha), deve ser escrita
uma varivel registro ou linha como a expresso.

RETURN

O valor retornado pela funo no pode ser deixado indefinido.


Se o controle atingir o final do bloco de nvel mais alto da
funo sem atingir uma instruo RETURN, ocorrer um erro
em tempo de execuo.

RETURN
CREATE FUNCTION soma(a INTEGER, b INTEGER) RETURNS
INTEGER AS
$$
DECLARE
res INTEGER;
BEGIN
res := a + b;
return res;
END;
$$
LANGUAGE plpgsql;

RETURN NEXT

RETURN NEXT expresso;


Quando uma funo PL/pgSQL declarada como
retornando SETOF, o procedimento a ser seguido um
pouco diferente.
Neste caso, os itens individuais a serem retornados so
especificados em comandos RETURN NEXT, e um comando
RETURN final, sem nenhum argumento, utilizado para
indicar que a funo chegou ao fim de sua execuo.
O comando RETURN NEXT pode ser utilizado tanto com
tipos de dado escalares quanto compostos.

RETURN NEXT

As funes que utilizam RETURN NEXT devem ser


chamadas da seguinte maneira:
SELECT * FROM alguma_funo();

RETURN NEXT
CREATE OR REPLACE FUNCTION listaEmpregado() RETURNS
SETOF varchar AS
$$
DECLARE
registro RECORD;
BEGIN
FOR registro IN SELECT * FROM empregado LOOP
RETURN NEXT registro.emp_nome;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql';

Condicionais

As instrues IF permitem executar os comandos com base


em certas condies. A linguagem PL/pgSQL possui cinco
formas de IF:
IF ... THEN
IF ... THEN ... ELSE
IF ... THEN ... ELSE IF
IF ... THEN ... ELSIF ... THEN ... ELSE
IF ... THEN ... ELSEIF ... THEN ... ELSE

IF-THEN
IF expresso_booleana THEN
instrues
END IF;

As instrues IF-THEN so a forma mais simples de IF. As


instrues entre o THEN e o END IF so executadas se a
condio for verdade. Seno, so saltadas.
Exemplo:
IF v_id_usuario <> 0 THEN
UPDATE usuarios
SET email = v_email WHERE id_usuario =
v_id_usuario;
END IF;

IF-THEN-ELSE
IF expresso_booleana THEN instrues
ELSE instrues
END IF;

As instrues IF-THEN-ELSE ampliam o IF-THEN permitindo


especificar um conjunto alternativo de instrues a serem
executadas se a condio for avaliada como falsa.;
IF v_contador > 0 THEN
INSERT INTO contador_de_usurios (contador)
VALUES (v_contador);
RETURN 't';
ELSE
RETURN 'f';
END IF;

IF-THEN-ELSE IF

As instrues IF podem ser aninhadas, como no seguinte


exemplo:
IF linha_demo.sexo = 'm' THEN
sexo_extenso := 'masculino';
ELSE
IF linha_demo.sexo = 'f' THEN
sexo_extenso := 'feminino';
END IF;
END IF;

IF-THEN-ELSIF-ELSE
IF expresso_booleana THEN
instrues
[ ELSIF expresso_booleana THEN
instrues
[ ELSIF expresso_booleana THEN
instrues
...]]
[ ELSE instrues ]
END IF;

IF-THEN-ELSIF-ELSE Exemplo
IF numero = 0 THEN
resultado := 'zero';
ELSIF numero > 0 THEN
resultado := 'positivo';
ELSIF numero < 0 THEN
resultado := 'negativo';
ELSE
resultado := 'NULL';
END IF;

Estruturas de repetio

Com as instrues abaixo pode-se fazer uma funo


PL/pgSQL repetir uma srie de comandos:

LOOP

EXIT

WHILE

FOR

LOOP
LOOP
instrues
END LOOP;

A instruo LOOP define um lao incondicional, repetido


indefinidamente at ser terminado por uma instruo EXIT
ou RETURN.

EXIT

EXIT [ rtulo ][ WHEN expresso ];


Quando WHEN est presente, a sada do lao ocorre
somente se a condio especificada for verdadeira, seno
o controle passa para a instruo aps o EXIT.
Pode ser utilizado EXIT para causar uma sada prematura de
qualquer tipo de lao.

Exemplo 1
LOOP
-- algum processamento
IF contador > 0 THEN
EXIT; -- sair do lao
END IF;
END LOOP;

Exemplo 2
LOOP
-- algum processamento
-- mesmo resultado do exemplo anterior
EXIT WHEN contador > 0;
END LOOP;

WHILE
WHILE expresso LOOP
instrues
END LOOP;

A instruo WHILE repete uma seqncia de instrues


enquanto a expresso de condio for avaliada como
verdade.
A condio verificada logo antes de cada entrada no
corpo do lao.

Exemplos
WHILE quantia_devida > 0 LOOP
-- algum processamento
END LOOP;

WHILE NOT expresso_booleana LOOP


-- algum processamento
END LOOP;

FOR
FOR nome IN [ REVERSE ] expresso .. expresso LOOP
instrues
END LOOP;

Esta forma do FOR cria um lao que interage num intervalo


de valores inteiros.
A varivel nome definida automaticamente como sendo
do tipo integer, e somente existe dentro do lao.
As duas expresses que fornecem o limite inferior e
superior do intervalo so avaliadas somente uma vez, ao
entrar no lao. Normalmente o passo da interao 1,
mas quando REVERSE especificado se torna -1.

Exemplo
FOR i IN 1..10 LOOP
-- algum processamento
RAISE NOTICE 'i %', i;
END LOOP;

FOR i IN REVERSE 10..1 LOOP


-- algum processamento
END LOOP;

Exemplo
CREATE OR REPLACE FUNCTION REVERSE ( str text ) RETURNS
text AS
$$
DECLARE
s text := ''; i int;
BEGIN
FOR i IN REVERSE length(str) .. 1 LOOP
s := s || substr(str,i,1);
END LOOP;
RETURN s;
END;
$$ LANGUAGE plpgsql;

Lao atravs do resultado da consulta

Utilizando um tipo diferente de lao FOR, possvel


interagir atravs do resultado de uma consulta e
manipular os dados.
A sintaxe :
FOR registro_ou_linha IN comando LOOP
instrues
END LOOP;

Cada linha de resultado do comando (que deve ser um


SELECT) atribuda, sucessivamente, varivel registro ou
linha, e o corpo do lao executado uma vez para cada
linha.

Exemplo
CREATE OR REPLACE FUNCTION contador_cli () RETURNS
INTEGER AS
$$
DECLARE
registro RECORD; qtde INTEGER;
BEGIN
qtde := 0;
FOR registro IN SELECT * FROM Cliente LOOP
qtde := qtde + 1;
RAISE NOTICE ' nome: %, cidade: %' , registro.nome_cli,
registro.cidade;
END LOOP;
RETURN qtde;
END;
$$ LANGUAGE 'plpgsql';

Captura de erros

Por padro, qualquer erro que ocorra em uma funo


PL/pgSQL interrompe a execuo da funo.
possvel capturar e se recuperar de erros utilizando um bloco
BEGIN com a clusula EXCEPTION. A sintaxe :
BEGIN
instrues
EXCEPTION
WHEN condio [ OR condio ... ] THEN
instrues_do_tratador
END;

Captura de erros

Quando um erro capturado pela clusula EXCEPTION, as


variveis locais da funo PL/pgSQL permanecem como
estavam quando o erro ocorreu, mas todas as modificaes
no estado persistente do banco de dados dentro do bloco
so desfeitas. Como exemplo, consideremos este fragmento
de cdigo:
INSERT INTO minha_tabela(nome, sobrenome)
VALUES('Tom', 'Jones');
BEGIN
UPDATE minha_tabela SET nome = 'Joe' WHERE
sobrenome = 'Jones';
x := x + 1; y := x / 0;
EXCEPTION
WHEN division_by_zero THEN RAISE NOTICE
division_by_zero';
RETURN x;

Estudo de caso - prtica

Você também pode gostar