Todas as variáveis usadas em um bloco devem ser declaradas na seção
de declarações do bloco.
(As únicas exceções são a variável de laço, de um laço
FOR iterando sobre um intervalo de valores
inteiros, declarada automaticamente como uma variável inteira
e, da mesma forma, a variável de laço, de um laço
FOR iterando sobre o resultado de um cursor,
declarada automaticamente como variável de registro.)
As variáveis do PL/pgSQL podem ter qualquer
tipo de dados SQL, como integer,
varchar e char.
Aqui estão alguns exemplos de declarações de variáveis:
id_usuario integer; quantidade numeric(5); url varchar; minhalinha nomedatabela%ROWTYPE; meucampo nomedatabela.nomedacoluna%TYPE; umalinha RECORD;
A sintaxe geral de uma declaração de variável é:
nome[ CONSTANT ]tipo_de_dados[ COLLATEnome_da_ordenação] [ NOT NULL ] [ { DEFAULT | := | = }expressão];
A cláusula DEFAULT, se fornecida, especifica
o valor inicial atribuído à variável ao entrar no bloco.
Se a cláusula DEFAULT não for fornecida, a
variável será inicializada com o valor nulo do SQL.
A opção CONSTANT evita que a variável receba
atribuição após ser inicializada, de forma que seu valor permaneça
constante durante todo o bloco.
A opção COLLATE especifica a ordenação a ser
usada para a variável
(veja Ordenação das variáveis do PL/pgSQL).
Se for especificado NOT NULL, a atribuição de
um valor nulo resultará em erro de tempo de execução.
Todas as variáveis declaradas como NOT NULL
devem ter um valor padrão não nulo especificado.
Pode ser usado o sinal de igual (=), em vez do
:= compatível com o
PL/SQL
[123].
O valor padrão de uma variável é avaliado e atribuído à variável
toda vez que entrar no bloco
(não apenas uma vez por chamada de função).
Assim, por exemplo, atribuir now() a uma
variável do tipo de dados timestamp faz com que a
variável receba a hora corrente da chamada da função, e não a
hora em que a função foi pré-compilada.
Exemplos:
quantidade integer DEFAULT 32; url varchar := 'http://mysite.com'; hora_da_transacao CONSTANT timestamp with time zone := now();
Uma vez declarada, o valor de uma variável pode ser usado em expressões de inicialização posteriores no mesmo bloco como, por exemplo:
DECLARE x integer := 1; y integer := x + 1;
Os parâmetros passados para as funções recebem os nomes
identificadores $1, $2, etc.
Opcionalmente, podem ser declarados aliases para os nomes dos
parâmetros $ para
melhorar a legibilidade.
Tanto o alias quanto o identificador numérico podem ser usados
para se referir ao valor do parâmetro.
n
Existem duas maneiras de criar um alias.
A forma preferida é dando nome ao parâmetro no comando
CREATE FUNCTION. Por exemplo:
CREATE FUNCTION imposto_vendas(subtotal real) RETURNS real AS $$
BEGIN
RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;
A outra maneira é declarar explicitamente o alias, usando a sintaxe de declaração
nomeALIAS FOR $n;
O mesmo exemplo escrito neste estilo se parece com:
CREATE FUNCTION imposto_vendas(real) RETURNS real AS $$
DECLARE
subtotal ALIAS FOR $1;
BEGIN
RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;
Estes dois exemplos não são perfeitamente equivalentes.
No primeiro exemplo, subtotal pode ser
referenciado como imposto_vendas.subtotal,
mas no segundo exemplo não.
(Se tivesse sido anexado um rótulo ao bloco interno,
subtotal poderia ser qualificado com este
rótulo.)
Mais alguns exemplos:
CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
v_string ALIAS FOR $1;
index ALIAS FOR $2;
BEGIN
-- alguns cálculos usando v_string e index aqui
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION concat_campos_selec(in_t nome_alguma_tabela) RETURNS text AS $$
BEGIN
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;
Quando uma função PL/pgSQL é declarada
com parâmetros de saída, os parâmetros de saída recebem nomes
$ e aliases
opcionais, da mesma maneira que os parâmetros de entrada normais.
Um parâmetro de saída é de fato uma variável cujo valor
inicial é NULL; o valor deve ser atribuído durante a execução da
função. É retornado o valor final do parâmetro.
Por exemplo, a função nimposto_vendas acima
também pode ser declarada da seguinte forma:
CREATE FUNCTION imposto_vendas(subtotal real, OUT tax real) AS $$
BEGIN
tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;
Note que foi omitido RETURNS real na função
— poderia ter sido incluído, mas seria redundante.
Na chamada de uma função com parâmetros OUT,
o(s) parâmetro(s) de saída devem ser omitidos:
SELECT imposto_vendas(100.00);
Parâmetros de saída são mais úteis quando são retornados vários valores. Um exemplo trivial é:
CREATE FUNCTION soma_produto(x int, y int, OUT soma int, OUT prod int) AS $$
BEGIN
soma := x + y;
prod := x * y;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM soma_produto(2, 4);
soma | prod
------+------
6 | 8
(1 linha)
Conforme discutido em Funções SQL com parâmetros de saída,
os parâmetros de saída criam de fato um tipo de dados registro
anônimo para os resultados da função.
Se for especificada a cláusula RETURNS, deverá
ser RETURNS record.
Também funciona com procedimentos. Por exemplo [124]:
CREATE PROCEDURE soma_produto(x int, y int, OUT soma int, OUT prod int) AS $$
BEGIN
soma := x + y;
prod := x * y;
END;
$$ LANGUAGE plpgsql;
Na chamada do procedimento, todos os parâmetros devem ser
especificados. Pode ser especificado NULL para
os parâmetros de saída ao se chamar o procedimento em uma
instrução SQL simples:
CALL soma_produto(2, 4, NULL, NULL);
soma | prod
------+------
6 | 8
(1 linha)
Entretanto, ao se chamar o procedimento a partir do código PL/pgSQL, deve ser declarada uma variável para cada parâmetro de saída; cada variável receberá o resultado da chamada. Veja Chamada de procedimento para obter detalhes.
Outra forma de declarar uma função PL/pgSQL
é usando RETURNS TABLE. Por exemplo:
CREATE FUNCTION vendas_estendidas(p_itemno int)
RETURNS TABLE(quantidade int, total numeric) AS $$
BEGIN
RETURN QUERY SELECT s.quantidade, s.quantidade * s.preco FROM vendas AS s
WHERE s.itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;
Esta forma equivale a declarar um ou mais
parâmetros OUT, e especificar
RETURNS SETOF .
algumtipo
Quando o tipo de dados retornado por uma função
PL/pgSQL é declarado como tipo de dados
polimórfico (veja Tipos de dados polimórficos),
é criado um parâmetro especial $0.
Seu tipo de dados é o tipo de dados real retornado pela função,
conforme deduzido dos tipos de dados de entrada reais.
Isto permite que a função acesse seu tipo de dados real retornado,
conforme mostrado em Cópia de tipo de dados.
O parâmetro especial $0 é inicializado como nulo,
podendo ser modificado pela função, portanto, pode ser usado para
guardar o valor retornado se desejado, embora isto não seja
obrigatório.
$0 também pode receber um alias.
Por exemplo, esta função funciona com qualquer tipo de dados que
possua o operador +:
CREATE FUNCTION soma_tres_valores(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
resultado ALIAS FOR $0;
BEGIN
resultado := v1 + v2 + v3;
RETURN resultado;
END;
$$ LANGUAGE plpgsql;
O mesmo efeito pode ser obtido declarando um ou mais parâmetros
de saída com tipos de dados polimórficos.
Neste caso, o parâmetro especial $0 não é usado;
os próprios parâmetros de saída servem ao mesmo propósito.
Por exemplo:
CREATE FUNCTION soma_tres_valores(v1 anyelement, v2 anyelement, v3 anyelement,
OUT soma anyelement)
AS $$
BEGIN
soma := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;
Na prática, pode ser mais útil declarar uma função polimórfica
usando a família de tipos de dados anycompatible,
para que ocorra a promoção automática dos argumentos de entrada
para um tipo de dados comum. Por exemplo:
CREATE FUNCTION soma_tres_valores(v1 anycompatible, v2 anycompatible, v3 anycompatible)
RETURNS anycompatible AS $$
BEGIN
RETURN v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;
Neste exemplo, uma chamada como
SELECT soma_tres_valores(1, 2, 4.7);
soma_tres_valores
-------------------
7.7
(1 linha)
funciona, promovendo automaticamente as entradas de inteiro
para numérico.
Uma função usando o tipo de dados anyelement exigiria
que se convertesse as três entradas para o mesmo tipo de dados
manualmente.
nome_novoALIAS FORnome_antigo;
A sintaxe de ALIAS é mais geral do que o
sugerido na seção anterior: pode ser declarado um alias para
qualquer variável, e não apenas para parâmetros de função.
O principal uso prático para o alias é atribuir um nome diferente
para variáveis com nomes predeterminados, como
NEW ou OLD,
numa função de gatilho.
Exemplos:
DECLARE anterior ALIAS FOR old; atualizado ALIAS FOR new;
Como o ALIAS cria duas maneiras diferentes para
referenciar o mesmo objeto, o uso irrestrito pode ficar confuso.
É melhor usá-lo apenas com a finalidade de substituir nomes
predeterminados.
nometabela.coluna%TYPEnomevariável%TYPE
%TYPE fornece o tipo de dados de uma coluna de
tabela ou de uma variável do PL/pgSQL
previamente declarada.
Pode-se usar isto para declarar variáveis que armazenarão valores
do banco de dados.
Por exemplo, digamos que se tenha uma coluna chamada
user_id na tabela users.
Para declarar uma variável com o mesmo tipo de dados que
users.user_id se escreve:
user_id users.user_id%TYPE;
Também é possível escrever uma especificação de matriz após
%TYPE, criando assim uma variável que armazena
uma matriz do tipo de dados referenciado:
user_ids users.user_id%TYPE[]; user_ids users.user_id%TYPE ARRAY[4]; -- equivalente à acima
Assim como na declaração de colunas de tabela que são matrizes, não importa se são escritos vários pares de colchetes ou dimensões de matriz específicas: O PostgreSQL trata todas as matrizes de um determinado tipo de dados de elemento como sendo do mesmo tipo de dados, independentemente da dimensionalidade. (Veja Declaração dos tipos de dados matriz.)
Ao usar %TYPE não é necessário conhecer o tipo de
dados da estrutura que se está referenciando, e mais importante,
se o tipo de dados do item referenciado mudar no futuro
(por exemplo: o tipo de dados de user_id
for mudado de integer para real),
talvez não seja necessário alterar a definição da função.
%TYPE é particularmente valioso em funções
polimórficas, porque os tipos de dados necessários para as
variáveis internas podem mudar de uma chamada para outra.
Podem ser criadas variáveis apropriadas aplicando
%TYPE aos argumentos da função, ou a marcadores
de lugar (placeholders) do resultado.
nomenome_da_tabela%ROWTYPE;nomenome_do_tipo_de_dados_composto;
Uma variável de tipo de dados composto é chamada de variável
linha
(ou uma variável tipo-linha).
Esta variável pode conter uma linha inteira do resultado da
consulta SELECT ou FOR, desde
que o conjunto de colunas dessa consulta corresponda ao tipo de dados
declarado da variável. Os valores individuais dos campos da linha
são acessados usando a notação de ponto usual, por exemplo,
var_linha.campo.
Uma variável tipo-linha pode ser declarada tendo o mesmo tipo de
dados das linhas de uma tabela ou visão existente, usando a notação
nome_da_tabela%ROWTYPE;
ou pode ser declarada fornecendo o nome de um tipo de dados composto.
(Uma vez que toda tabela tem um tipo de dados composto associado
com o mesmo nome da tabela, na verdade, não importa para o
PostgreSQL se for escrito
%ROWTYPE ou não, mas a forma com
%ROWTYPE é mais portável.)
Assim como em %TYPE, %ROWTYPE
pode ser seguido por uma declaração de matriz para declarar uma
variável que armazena uma matriz do tipo de dados composto referenciado.
Os parâmetros de uma função podem ser de tipos de dados compostos
(linhas inteiras da tabela). Neste caso, o identificador
correspondente $
será uma variável tipo-linha, e os campos podem ser selecionados
a partir dela, por exemplo, n$1.user_id.
A seguir está um exemplo de uso de tipos de dados compostos.
tabela1 e tabela2
são tabelas existentes com ao menos os campos mencionados:
CREATE FUNCTION concatena_campos(t_linha tabela1) RETURNS text AS $$
DECLARE
t2_linha tabela2%ROWTYPE;
BEGIN
SELECT * INTO t2_linha FROM tabela2 WHERE ... ;
RETURN t_linha.f1 || t2_linha.f3 || t_linha.f5 || t2_linha.f7;
END;
$$ LANGUAGE plpgsql;
SELECT concatena_campos(t.*) FROM tabela1 t WHERE ... ;
nome RECORD;
As variáveis tipo-registro são semelhantes às variáveis tipo-linha,
mas não possuem uma estrutura predefinida.
Elas assumem a estrutura da linha que lhes foi atribuída durante
o comando SELECT ou FOR.
A subestrutura de uma variável tipo-registro pode mudar cada vez
que a variável receber uma atribuição.
Uma consequência disso é que, até que a variável tipo-registro tenha
recebido uma atribuição pela primeira vez, ela não possui subestrutura,
e qualquer tentativa de acessar um campo dela resultará em erro
de tempo de execução.
Note que RECORD não é um tipo de dados verdadeiro,
é apenas um marcador de lugar (placeholder).
Deve-se, também, perceber que quando uma função
PL/pgSQL é declarada retornando o tipo
de dados record, este não é exatamente o mesmo conceito
de uma variável do tipo-registro, embora a função possa usar
uma variável do tipo-registro para armazenar seu resultado.
Nos dois casos, a estrutura real da linha é desconhecida quando do
momento da declaração, mas no caso de uma função que retorna
record, a estrutura real é determinada no momento de
análise da chamada da consulta, enquanto uma variável do
tipo-registro pode alterar sua estrutura de linha dinamicamente.
Quando uma função PL/pgSQL possui um ou mais parâmetros com tipos de dados ordenáveis, é identificada uma ordenação (collation) para cada chamada da função, dependendo das ordenações atribuídas aos argumentos reais, conforme descrito em Suporte a ordenação. Se uma ordenação for identificada com êxito (ou seja, não houver conflitos de ordenações implícitos entre os argumentos), todos os parâmetros que podem ser ordenados serão tratados como tendo este ordenamento implicitamente. Isto afeta o comportamento das operações sensíveis à ordenação dentro da função. Por exemplo, considere
CREATE FUNCTION menor_que(a text, b text) RETURNS boolean AS $$
BEGIN
RETURN a < b;
END;
$$ LANGUAGE plpgsql;
SELECT menor_que(text_field_1, text_field_2) FROM table1;
SELECT menor_que(text_field_1, text_field_2 COLLATE "C") FROM table1;
A primeira chamada da função menor_que usará a
ordenação comum de text_field_1 e
text_field_2 para a comparação,
enquanto a segunda chamada usará a ordenação C.
Além disso, a ordenação identificada também é assumida como a ordenação de quaisquer variáveis locais que sejam de tipos de dados ordenáveis. Portanto, esta função não funcionaria de maneira diferente se fosse escrita como
CREATE FUNCTION menor_que(a text, b text) RETURNS boolean AS $$
DECLARE
local_a text := a;
local_b text := b;
BEGIN
RETURN local_a < local_b;
END;
$$ LANGUAGE plpgsql;
Não havendo parâmetros de tipos de dados ordenáveis, ou nenhuma ordenação comum puder ser identificada para eles, os parâmetros e as variáveis locais usarão a ordenação padrão de seu tipo de dados (que é geralmente a ordenação padrão do banco de dados, mas pode ser diferente para variáveis de tipos de dados de domínio).
Uma variável local de um tipo de dados ordenável pode ter uma
ordenação diferente associada a ela, incluindo a opção
COLLATE em sua declaração. Por exemplo:
DECLARE
local_a text COLLATE "en_US";
Esta opção substitui a ordenação que seria dada à variável segundo as regras acima.
Além disso, é claro que podem ser escritas cláusulas
COLLATE explícitas dentro da função,
se for desejado forçar o uso de uma ordenação específica
em uma operação específica. Por exemplo:
CREATE FUNCTION menor_que_c(a text, b text) RETURNS boolean AS $$
BEGIN
RETURN a < b COLLATE "C";
END;
$$ LANGUAGE plpgsql;
SELECT menor_que_c ('ABC', 'abc');
menor_que_c ------------- t (1 linha)
Esta cláusula substitui as ordenações associados às colunas da tabela, parâmetros ou variáveis locais usadas na expressão, exatamente como aconteceria em um comando SQL simples.
Exemplo 41.1. Exemplo do tradutor
Comparação usando a ordenação pt_BR
Neste exemplo é criada a função menor_que_pt_br,
que compara os dois argumentos da entrada usando a ordenação
“pt_BR”, para comparar com o resultado da função
menor_que_c, que usa a ordenação C.
Como pode ser visto pelos resultados, na ordenação C
ABC é menor que (vem antes de) abc,
enquanto na ordenação “pt_BR” se dá o contrário.
CREATE FUNCTION menor_que_pt_br(a text, b text) RETURNS boolean AS $$
BEGIN
RETURN a < b COLLATE "pt_BR";
END;
$$ LANGUAGE plpgsql;
SELECT menor_que_pt_BR ('ABC', 'abc');
menor_que_pt_br ----------------- f (1 linha)
[123] PL/SQL significa “Extensões de Linguagem Procedural para a Linguagem de Consulta Estruturada” (Procedural Language Extensions to the Structured Query Language). O PL/SQL adiciona muitas construções procedurais à linguagem SQL para superar algumas limitações do SQL. Além disso, o PL/SQL fornece uma solução de linguagem de programação mais abrangente para criar aplicações de missão crítica em bancos de dados Oracle. Oracle PL/SQL Tutorial (N. T.)
[124] Não pode existir uma função e um procedimento com o mesmo nome. Para o procedimento ser criado, a função com o mesmo nome deve ser excluída. (N. T.)