36.5. Funções na linguagem de consulta (SQL) #

36.5.1. Argumentos para funções SQL
36.5.2. Funções SQL com tipos de dados base
36.5.3. Funções SQL com tipos de dados compostos
36.5.4. Funções SQL com parâmetros de saída
36.5.5. Procedimentos SQL com parâmetros de saída
36.5.6. Funções SQL com número variável de argumentos
36.5.7. Funções SQL com valor padrão para argumentos
36.5.8. Funções SQL como fonte de tabela
36.5.9. Funções SQL retornando conjuntos
36.5.10. Funções SQL retornando TABLE
36.5.11. Funções SQL polimórficas
36.5.12. Funções SQL com ordenação

As funções SQL executam uma lista arbitrária de instruções SQL, retornando o resultado da última consulta numa lista. No caso mais simples (não conjunto), será retornada a primeira linha do resultado da última consulta. (Tenha em mente que a primeira linha de um resultado de várias linhas não fica bem definido, a menos que seja usado ORDER BY.) Se a última consulta não retornar nenhuma linha, será retornado o valor nulo.

Como alternativa, a função SQL pode ser declarada retornando um conjunto (ou seja, várias linhas), especificando o tipo de dados retornado pela função como SETOF algum_tipo, ou de forma equivalente, declarando a função com RETURNS TABLE(colunas). Nesses casos, são retornadas todas as linhas do resultado da última consulta. Abaixo são mostrados mais detalhes.

O corpo de uma função SQL deve ser uma lista de instruções SQL separadas por ponto e vírgula. O ponto e vírgula após a última instrução é opcional. A menos que a função seja declarada retornando void, a última declaração deve ser um SELECT, ou um INSERT, UPDATE, DELETE ou MERGE contendo a cláusula RETURNING.

Qualquer coleção de comandos na linguagem SQL pode ser agrupada e definida como uma função. Além das consultas SELECT, os comandos podem incluir instruções de modificação de dados (INSERT, UPDATE, DELETE e MERGE), assim como outros comandos SQL. (Não podem ser usados comandos de controle de transação, por exemplo, COMMIT, SAVEPOINT, e alguns comandos utilitários, por exemplo, VACUUM, nas funções SQL.) Entretanto, o comando final deve ser um SELECT, ou ter uma cláusula RETURNING retornando o que foi especificado como o tipo de dados retornado pela função. Como alternativa, se for desejado definir uma função SQL que execute ações, mas não tenha nenhum valor útil a ser retornado, esta função pode ser definida como retornando void. Por exemplo, a seguinte função remove as linhas com salários negativos da tabela emp:

CREATE FUNCTION clean_emp() RETURNS void AS '
    DELETE FROM emp
        WHERE salary < 0;
' LANGUAGE SQL;

SELECT clean_emp();

 clean_emp
-----------

(1 linha)

Como alternativa, a função poderia ser escrita como um procedimento, evitando assim o problema do tipo de dados retornado. Por exemplo:

CREATE PROCEDURE clean_emp() AS '
    DELETE FROM emp
        WHERE salary < 0;
' LANGUAGE SQL;

CALL clean_emp();

Em casos simples como este, a diferença entre uma função retornando void e um procedimento, é principalmente de estilo. Entretanto, os procedimentos oferecem funcionalidades adicionais, como controle de transações, que não estão disponíveis nas funções. Além disso, os procedimentos fazem parte do padrão SQL, enquanto retornar void é uma extensão do PostgreSQL.

A sintaxe do comando CREATE FUNCTION requer que o corpo da função seja escrito como uma constante cadeia de caracteres. Geralmente, é mais conveniente usar a delimitação por cifrão (veja Constantes do tipo cadeia de caracteres delimitadas por cifrão) para a constante cadeia de caracteres. Se for usada a sintaxe de constante cadeia de caracteres entre apóstrofos, os apóstrofos (') e contrabarras (\) no corpo da função deverão ser dobrados (assumindo que esteja sendo usada a sintaxe de escape na cadeia de caracteres) (veja Constantes do tipo cadeia de caracteres).

36.5.1. Argumentos para funções SQL #

Os argumentos de uma função SQL podem ser referenciados no corpo da função usando seus nomes ou números. Exemplos dos dois métodos são mostrados abaixo.

Para usar nome, o argumento da função deve ser declarado como tendo um nome e, em seguida, este nome deve ser escrito no corpo da função. Se o nome do argumento for igual a qualquer nome de coluna no comando SQL atual da função, o nome da coluna terá precedência. Para mudar este comportamento, o nome do argumento deve ser qualificado com o nome da própria função, ficando nome_da_função.nome_do_argumento. (Se esta qualificação entrar em conflito com um nome de coluna qualificado, novamente o nome da coluna terá precedência. A ambiguidade pode ser evitada escolhendo um alias diferente para a tabela dentro do comando SQL.)

Na abordagem numérica mais antiga, os argumentos são referenciados usando a sintaxe $n: $1 refere-se ao primeiro argumento de entrada, $2 ao segundo, e assim por diante. Isto funciona independentemente do argumento ter sido declarado com nome, ou não.

Se o argumento for de tipo de dados composto, então a notação de ponto, por exemplo, nome_do_argumento.nome_do_campo, ou $1.nome_do_campo, poderá ser usada para acessar os atributos do argumento. Novamente, pode ser necessário qualificar o nome do argumento com o nome da função, para tornar a forma com nome do argumento inequívoca.

Os argumentos da função SQL só podem ser usados como valores de dados, e não como identificadores. Assim, por exemplo, este comando é possível:

INSERT INTO minha_tabela VALUES ($1);

mas este não vai funcionar:

INSERT INTO $1 VALUES (42);

Nota

A capacidade de usar nomes para referenciar argumentos de funções SQL foi adicionada no PostgreSQL 9.2. As funções a serem usadas em servidores mais antigos devem usar a notação $n.

36.5.2. Funções SQL com tipos de dados base #

A função SQL mais simples possível não possui argumentos, e apenas retorna um tipo de dados base, como integer:

CREATE FUNCTION um() RETURNS integer AS $$
    SELECT 1 AS resultado;
$$ LANGUAGE SQL;

-- Sintaxe alternativa usando apóstrofos:
CREATE FUNCTION um() RETURNS integer AS '
    SELECT 1 AS resultado;
' LANGUAGE SQL;

SELECT um();

 um
----
  1
(1 linha)

Deve ser observado que, dentro do corpo da função acima, foi definido um alias de coluna para o resultado da função (com o nome resultado), mas este alias de coluna não é visível fora da função. Por isto, o resultado tem o rótulo um, em vez de resultado.

Também é fácil definir funções SQL que usam argumentos com tipos de dados base:

CREATE FUNCTION soma_num(x integer, y integer) RETURNS integer AS $$
    SELECT x + y;
$$ LANGUAGE SQL;

SELECT soma_num(1, 2) AS soma;

 soma
------
    3
(1 linha)

Como alternativa, os nomes dos argumentos poderiam ser dispensados e usados os números de suas posições:

CREATE FUNCTION soma_num(integer, integer) RETURNS integer AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

SELECT soma_num(1, 2) AS soma;

 soma
------
    3
(1 linha)

A seguir está uma função mais útil, que pode ser usada para realizar débito em uma conta bancária:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT 1;
$$ LANGUAGE SQL;

Um usuário pode executar esta função para debitar $100,00 da conta 17 da seguinte forma:

SELECT tf1(17, 100.0);

Neste exemplo, foi escolhido o nome accountno para o primeiro argumento, mas é idêntico ao nome de uma coluna na tabela bank. Dentro do comando UPDATE, accountno refere-se à coluna bank.accountno, portanto deve ser usado tf1.accountno para se referir ao argumento. É claro que isto pode ser evitado usando um nome diferente para o argumento.

Na prática, provavelmente se gostaria de obter um resultado mais útil da função do que a constante 1, portanto, uma definição mais razoável seria

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT balance FROM bank WHERE accountno = tf1.accountno;
$$ LANGUAGE SQL;

que ajusta o saldo e retorna o novo saldo. O mesmo resultado pode ser obtido usando a cláusula RETURNING:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno
    RETURNING balance;
$$ LANGUAGE SQL;

Se a cláusula final SELECT ou RETURNING em uma função SQL não retornar um resultado do mesmo tipo de dados declarado na função, o PostgreSQL converterá automaticamente o valor para o tipo de dados declarado, se isto for possível com uma conversão implícita ou de atribuição. Caso contrário, deve ser escrita uma conversão explícita. Por exemplo, se for desejado que a função soma_num anterior retorne o tipo de dados float8, bastará então escrever

CREATE FUNCTION soma_num(integer, integer) RETURNS float8 AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

já que a soma do tipo de dados integer pode ser convertida implicitamente para float8. (Veja Conversão de tipo de dados ou CREATE CAST para obter mais informações sobre conversões.)

36.5.3. Funções SQL com tipos de dados compostos #

Ao escrever funções com argumentos de tipos de dados compostos, não se deve apenas especificar qual argumento se deseja, mas também o atributo desejado (campo) desse argumento. Por exemplo, supondo que emp seja uma tabela contendo dados de funcionários, portanto, será também o nome do tipo de dados composto de cada linha da tabela. A seguir está mostrada a função dobrar_salário, que calcula qual seria o salário de alguém, se este fosse dobrado:

CREATE TABLE emp (
    nome        text,
    salário     numeric,
    idade       integer,
    baia        point
);

INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)');

CREATE FUNCTION dobrar_salário(emp) RETURNS numeric AS $$
    SELECT $1.salário * 2 AS salário;
$$ LANGUAGE SQL;

SELECT nome, dobrar_salário(emp.*) AS sonho
    FROM emp
    WHERE emp.baia ~= point '(2,1)';

 nome | sonho
------+-------
 Bill |  8400
(1 linha)

Deve ser observado o uso da sintaxe $1.salário para selecionar o campo salário da linha do argumento. Veja, também, que o comando SELECT chamando a função usa nome_da_tabela.* para selecionar toda a linha corrente da tabela como valor composto. A linha da tabela também pode ser referenciada usando apenas o nome da tabela, da seguinte forma

SELECT nome, dobrar_salário(emp) AS sonho
    FROM emp
    WHERE emp.baia ~= point '(2,1)';

mas este uso está em obsolescência (deprecated), porque é fácil causar confusão. (Veja Uso de tipos de dados compostos em consultas para obter detalhes sobre estas duas notações para o valor composto de uma linha da tabela.)

Às vezes, é útil construir o valor do argumento composto em tempo de execução (on the fly). Isto pode ser feito com a construção ROW. Por exemplo, pode-se ajustar os dados sendo passados para a função:

SELECT nome, dobrar_salário(ROW(nome, salário*1.1, idade, baia)) AS sonho
    FROM emp;

Também é possível construir uma função retornando um tipo de dados composto. A seguir está um exemplo de função que retorna uma única linha do tipo de dados emp:

CREATE FUNCTION novo_empregado() RETURNS emp AS $$
    SELECT text 'Ninguém' AS nome,
        1000.0 AS salário,
        25 AS idade,
        point '(2,2)' AS baia;
$$ LANGUAGE SQL;

Neste exemplo, cada um dos atributos é especificado usando um valor constante, mas estas constantes podem ser substituídas por algum cálculo.

Devem ser observadas duas coisas importantes sobre a definição da função:

  • A ordem da lista de seleção na consulta deve ser idêntica à ordem em que as colunas aparecem no tipo de dados composto. (Dar nomes às colunas, como foi feito acima, é irrelevante para o sistema.)

  • Deve-se garantir que o tipo de dados de cada expressão possa ser convertido para o tipo de dados da coluna correspondente do tipo de dados composto. Caso contrário, vão ser mostrados erros como este:

    ERRO:  O tipo de dados retornado não corresponde ao que foi declarado na função emp
    DETALHE:  O último comando retornou text ao invés de point na coluna 4.
    CONTEXTO:  função SQL "novo_empregado"
    

    Assim como no caso do tipo de dados base, o sistema não insere conversões explícitas automaticamente, realizando apenas conversões implícitas ou de atribuição.

Uma forma diferente de definir a mesma função é:

CREATE FUNCTION novo_empregado() RETURNS emp AS $$
    SELECT ROW('Ninguém', 1000.0, 25, '(2,2)')::emp;
$$ LANGUAGE SQL;

Aqui foi escrito um comando SELECT retornando apenas uma única coluna do tipo de dados composto correto. Na verdade, isto não é melhor nesta situação, mas é uma alternativa útil em alguns casos — por exemplo, se for necessário calcular o resultado chamando outra função que retorne o valor composto desejado. Outro exemplo, é que se estivermos tentando escrever uma função que retorne um domínio sobre um tipo de dados composto, em vez de um tipo de dados composto simples, é sempre necessário escrevê-la como retornando uma única coluna, por não haver como fazer uma conversão de resultado da linha inteira.

Podemos chamar esta função diretamente, usando-a em uma expressão de valor:

SELECT novo_empregado();

       novo_empregado
-----------------------------
 (Ninguém,1000.0,25,"(2,2)")
(1 linha)

ou chamando-a como uma função de tabela:

SELECT * FROM novo_empregado();

  nome   | salário | idade | baia
---------+---------+-------+-------
 Ninguém |  1000.0 |    25 | (2,2)
(1 linha)

A segunda forma é descrita em mais detalhes em Funções SQL como fonte de tabela.

Ao usar uma função retornando tipo de dados composto, pode-se desejar apenas um campo (atributo) do resultado. Isto pode ser feito com uma sintaxe como esta:

SELECT (novo_empregado()).nome;

  nome
---------
 Ninguém
(1 linha)

Os parênteses extras são necessários para evitar que o analisador se confunda. Se for tentado fazer isto sem os parênteses extras, será retornado algo assim:

SELECT novo_empregado().nome;

ERRO:  erro de sintaxe em ou próximo a "."
LINHA 1: SELECT novo_empregado().nome;
                                ^

Outra opção é usar a notação de função para extrair um atributo:

SELECT nome(novo_empregado());

  nome
---------
 Ninguém
(1 linha)

Conforme explicado em Uso de tipos de dados compostos em consultas, a notação de campo e a notação de função são equivalentes.

Outra forma de usar uma função retornando tipo de dados composto, é passar o resultado para outra função que aceite o tipo de dados de linha correto como entrada:

CREATE FUNCTION traz_nome(emp) RETURNS text AS $$
    SELECT $1.nome;
$$ LANGUAGE SQL;

SELECT traz_nome(novo_empregado());

 traz_nome
-----------
 Ninguém
(1 linha)

36.5.4. Funções SQL com parâmetros de saída #

Uma forma alternativa de descrever os resultados de uma função é defini-la com parâmetros de saída, como neste exemplo:

CREATE FUNCTION soma_int (IN x int, IN y int, OUT sum int)
AS 'SELECT x + y'
LANGUAGE SQL;

SELECT soma_int(3,7);

 soma_int
----------
       10
(1 linha)

Esta função não é essencialmente diferente da versão de soma_int mostrada em Funções SQL em tipos de dados base. A verdadeira vantagem dos parâmetros de saída, é o fato de fornecerem uma forma conveniente de definir funções que retornam várias colunas. Por exemplo:

CREATE FUNCTION soma_prod_int (x int, y int, OUT soma int, OUT produto int)
AS 'SELECT x + y, x * y'
LANGUAGE SQL;

SELECT * FROM soma_prod_int(11, 42);

 soma | produto
------+---------
   53 |     462
(1 linha)

O que basicamente aconteceu nesta função, é que foi criado um tipo de dados composto anônimo para o resultado da função. O exemplo acima produz o mesmo resultado que

CREATE TYPE soma_prod AS (soma int, produto int);

CREATE FUNCTION soma_prod_int (int, int) RETURNS soma_prod
AS 'SELECT $1 + $2, $1 * $2'
LANGUAGE SQL;

SELECT * FROM soma_prod_int(11, 42);

 soma | produto
------+---------
   53 |     462
(1 linha)

porém, não haver necessidade de se preocupar com uma definição em separado do tipo de dados composto, é geralmente útil. Veja que os nomes anexados aos parâmetros de saída não são apenas decorativos, eles determinam os nomes das colunas do tipo de dados composto anônimo. (Se for omitido o nome de um parâmetro de saída, o sistema vai escolher um nome por conta própria.)

Deve-se observar que os parâmetros de saída não são incluídos na lista de argumentos de chamada, ao invocar uma função SQL. Isto acontece, porque o PostgreSQL considera apenas os parâmetros de entrada para definir a assinatura de chamada da função. Isto significa, também, que apenas os parâmetros de entrada importam ao referenciar a função para finalidades como eliminá-la. A função acima poderia ser excluída com qualquer um desses dois comandos:

DROP FUNCTION soma_prod_int (x int, y int, OUT sum int, OUT product int);
DROP FUNCTION sum_n_product (int, int);

Os parâmetros podem ser marcados como IN (o padrão), OUT, INOUT ou VARIADIC. Um parâmetro INOUT serve como parâmetro de entrada (parte da lista de argumentos de chamada), e parâmetro de saída (parte do tipo de dados registro do resultado). Os parâmetros VARIADIC são parâmetros de entrada, mas são tratados de forma especial conforme descrito mais abaixo.

36.5.5. Procedimentos SQL com parâmetros de saída #

Também há suporte para parâmetros de saída em procedimentos, mas funcionam de forma um pouco diferente das funções. Nos comandos CALL, os parâmetros de saída devem ser incluídos na lista de argumentos. Por exemplo, a rotina de débito em conta-corrente anterior poderia ser escrita como:

CREATE PROCEDURE tp1 (accountno integer, debit numeric, OUT new_balance numeric) AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tp1.accountno
    RETURNING balance;
$$ LANGUAGE SQL;

Para chamar este procedimento, deve ser incluído um argumento correspondente ao parâmetro OUT. É costume escrever NULL:

CALL tp1(17, 100.0, NULL);

Se for escrito alguma outra coisa, deve ser uma expressão que possa ser convertida implicitamente para o tipo de dados declarado do parâmetro, assim como para os parâmetros de entrada. No entanto, deve-se observar que esta expressão não será avaliada.

Ao chamar um procedimento a partir do PL/pgSQL, ao invés de escrever NULL deve ser escrito o nome da variável que vai receber a saída do procedimento. Veja Chamada de procedimento para obter mais detalhes.

36.5.6. Funções SQL com número variável de argumentos #

As funções SQL podem ser declaradas aceitando um número variável de argumentos, desde que todos os argumentos opcionais sejam do mesmo tipo de dados. Os argumentos opcionais são passados para a função como uma matriz. A função é declarada marcando o último parâmetro como VARIADIC; este parâmetro deve ser declarado como sendo do tipo de dados matriz. Por exemplo:

CREATE FUNCTION menor(VARIADIC arr numeric[]) RETURNS numeric AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT menor(10, -1, 5, 4.4);

 menor
-------
    -1
(1 linha)

De fato, todos os argumentos passados na posição VARIADIC, ou além, são reunidos em uma matriz unidimensional, como se tivesse sido escrito:

SELECT menor(ARRAY[10, -1, 5, 4.4]);    -- não funciona

Porém, não pode ser escrito assim — ou, pelo menos, não vai corresponder a esta definição de função. Um parâmetro marcado como VARIADIC corresponde a uma ou mais ocorrências de seu tipo de dados de elemento, não de seu próprio tipo de dados.

Às vezes é útil poder passar uma matriz já construída para uma função variádica; isto é particularmente útil quando a função variádica deseja passar seu parâmetro de matriz para outra. Além disso, esta é a única forma segura de chamar uma função variádica que se encontra em um esquema que permite que usuários não confiáveis criem objetos; veja Funções. Isto pode ser feito especificando VARIADIC na chamada:

SELECT menor(VARIADIC ARRAY[10, -1, 5, 4.4]);

Assim se evita a expansão do parâmetro variável da função em seu tipo de dados de elemento, permitindo que o valor do argumento da matriz corresponda normalmente. VARIADIC só pode ser anexado ao último argumento real de uma chamada de função.

Especificar VARIADIC na chamada também é a única forma de passar uma matriz vazia para uma função variádica. Por exemplo:

\pset null "<null>"
A exibição de nulos é ""<null>"".

SELECT menor(VARIADIC ARRAY[]::numeric[]);

  menor
----------
 "<null>"
(1 linha)

Escrever apenas SELECT menor() não funciona, porque o parâmetro variádico deve corresponder a pelo menos um argumento real. (Pode ser definida uma segunda função, também chamada menor, sem parâmetros, se for desejado permitir esta forma de chamada.)

Os parâmetros do elemento matriz gerados a partir de um parâmetro variádico são tratados como não tendo nomes próprios, significando não ser possível chamar uma função variádica usando argumentos com nomes (veja Chamadas de função), exceto quando for especificado VARIADIC. Por exemplo, este comando funciona:

SELECT menor(VARIADIC arr => ARRAY[10, -1, 5, 4.4]);

mas estes não:

SELECT menor(arr => 10);
SELECT menor(arr => ARRAY[10, -1, 5, 4.4]);

36.5.7. Funções SQL com valor padrão para argumentos #

As funções podem ser declaradas com valor padrão para alguns ou todos os argumentos de entrada. O valor padrão será inserido sempre que a função for chamada com argumentos reais insuficientes. Como os argumentos só podem ser omitidos do final da lista de argumentos, todos os parâmetros após um parâmetro com valor padrão também devem ter valor padrão. (Embora o uso da notação de argumento com nome possa permitir que esta restrição seja afrouxada, ela ainda é aplicada para que a notação de argumento posicional funcione de maneira consistente.) Independentemente de ser usado ou não, este recurso cria a necessidade de se tomar precauções ao chamar funções em bancos de dados onde alguns usuários não confiam em outros usuários; veja Funções.

Por exemplo:

CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
RETURNS int
LANGUAGE SQL
AS $$
    SELECT $1 + $2 + $3;
$$;

SELECT foo(10, 20, 30);
 foo
-----
  60
(1 linha)

SELECT foo(10, 20);
 foo
-----
  33
(1 linha)

SELECT foo(10);
 foo
-----
  15
(1 linha)

SELECT foo();  -- falha, por não existir valor padrão para o primeiro argumento
ERRO:  função foo() não existe
LINHA 1: SELECT foo();
                ^
DICA:  Nenhuma função corresponde com o nome e os tipos de argumentos informados.
       Você precisa adicionar conversões de tipo explícitas.

O sinal = também pode ser usado no lugar da palavra-chave DEFAULT.

36.5.8. Funções SQL como fonte de tabela #

Todas as funções SQL podem ser usadas na cláusula FROM de uma consulta, mas é particularmente útil para funções que retornam tipos de dados compostos. Se a função for definida retornando um tipo de dados base, a função de tabela produzirá uma tabela de uma coluna. Se a função for definida retornando um tipo de dados composto, a função de tabela produzirá uma coluna para cada atributo do tipo de dados composto.

A seguir está um exemplo:

CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES (1, 1, 'Joe');
INSERT INTO foo VALUES (1, 2, 'Ed');
INSERT INTO foo VALUES (2, 1, 'Mary');

CREATE FUNCTION getfoo(int) RETURNS foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT *, upper(fooname) FROM getfoo(1) AS t1;

 fooid | foosubid | fooname | upper
-------+----------+---------+-------
     1 |        1 | Joe     | JOE
(1 linha)

Como este exemplo mostra, pode-se trabalhar com as colunas do resultado da função da mesma forma como se fossem colunas de uma tabela normal.

Deve-se observar que a função retornou apenas uma linha. Isto se deve a não ter sido usado SETOF, como está descrito na próxima seção.

36.5.9. Funções SQL retornando conjuntos #

Quando uma função SQL é declarada retornando SETOF algum_tipo, a consulta final da função é executada até concluir, e cada linha gerada é retornada como um elemento do conjunto de resultados.

Este recurso é normalmente usado ao chamar a função na cláusula FROM. Neste caso, cada linha retornada pela função torna-se uma linha da tabela vista pela consulta. Por exemplo, supondo que a tabela foo tenha o mesmo conteúdo acima, e seja escrito

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

será obtido:

 fooid | foosubid | fooname
-------+----------+---------
     1 |        1 | Joe
     1 |        2 | Ed
(2 linhas)

Também é possível retornar várias linhas com as colunas definidas pelos parâmetros de saída, dessa forma:

CREATE TABLE tab (y int, z int);
INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8);

CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int)
RETURNS SETOF record
AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

SELECT * FROM sum_n_product_with_tab(10);

 sum | product
-----+---------
  11 |      10
  13 |      30
  15 |      50
  17 |      70
(4 linhas)

O ponto chave aqui, é que deve ser escrito RETURNS SETOF record para indicar que a função retorna várias linhas, em vez de apenas uma linha. Havendo apenas um parâmetro de saída, deve ser escrito o tipo de dados desse parâmetro, em vez de record.

Frequentemente, é útil construir o resultado de uma consulta chamando várias vezes uma função que retorna conjunto, com os parâmetros para cada chamada provenientes de linhas sucessivas de uma tabela ou sub-consulta. A forma preferida de fazer isto é usando a palavra-chave LATERAL, descrita em Subconsultas com LATERAL. A seguir está um exemplo usando uma função retornando conjunto para enumerar elementos de uma estrutura de árvore:

SELECT * FROM nodes;

   name    | parent
-----------+--------
 Top       |
 Child1    | Top
 Child2    | Top
 Child3    | Top
 SubChild1 | Child1
 SubChild2 | Child1
(6 linhas)

CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$
    SELECT name FROM nodes WHERE parent = $1
$$ LANGUAGE SQL STABLE;

SELECT * FROM listchildren('Top');

 listchildren
--------------
 Child1
 Child2
 Child3
(3 linhas)

SELECT name, child FROM nodes, LATERAL listchildren(name) AS child;

  name  |   child
--------+-----------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 linhas)

Este exemplo não faz nada que não poderia ser feito com uma simples junção, mas em cálculos mais complexos, a opção de colocar parte do trabalho em uma função pode ser bastante conveniente.

As funções retornando conjuntos também podem ser chamadas na lista de seleção de uma consulta. Para cada linha que a consulta gera sozinha, a função retornando conjunto é chamada, e uma linha de saída é gerada para cada elemento do conjunto de resultados da função. O exemplo anterior também poderia ser feito com consultas como estas:

SELECT listchildren('Top');

 listchildren
--------------
 Child1
 Child2
 Child3
(3 linhas)

SELECT name, listchildren(name) FROM nodes;

  name  | listchildren
--------+--------------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 linhas)

Deve-se observar que, no último SELECT não aparece nenhuma linha de saída para Child2, Child3, etc. Isto se dá, porque listchildren retorna um conjunto vazio para estes argumentos, portanto, nenhuma linha de resultado é gerada. Este é o mesmo comportamento obtido em uma junção interna para o resultado da função ao usar a sintaxe LATERAL.

O comportamento do PostgreSQL para uma função retornando conjunto na lista de seleção da consulta, é quase o mesmo que se a função retornando conjunto tivesse sido escrita em um item da cláusula LATERAL FROM. Por exemplo,

SELECT x, generate_series(1,5) AS g FROM tab;

é quase equivalente a

SELECT x, g FROM tab, LATERAL generate_series(1,5) AS g;

Seria exatamente o mesmo, exceto que neste exemplo específico, o planejador poderia escolher colocar g fora da junção de laço aninhado, já que g não tem dependência lateral na tabela tab. Isto resultaria em uma ordem diferente das linhas de saída. As funções retornando conjunto na lista de seleção são sempre avaliadas como se estivessem dentro da junção de laço aninhado com o restante da cláusula FROM, para que a(s) função(ões) seja(m) executada(s) até concluir(em), antes que a próxima linha da cláusula FROM seja considerada.

Havendo mais de uma função retornando conjunto na lista de seleção da consulta, o comportamento será semelhante ao obtido colocando as funções em um único item de cláusula. LATERAL ROWS FROM( ... ) FROM. Para cada linha da consulta subjacente, há uma linha de saída usando o primeiro resultado de cada função, depois uma linha de saída usando o segundo resultado, e assim por diante. Se algumas das funções retornando conjunto produzirem menos saídas do que outras, os dados ausentes serão substituídos por valores nulos, de modo que o número total de linhas emitidas para uma linha subjacente seja o mesmo da função retornando conjunto que produziu a maioria das saídas. Assim, as funções retornando conjunto são executadas em pari passu (lockstep), até que todas sejam esgotadas e, em seguida, a execução continua com a próxima linha subjacente.

As funções retornando conjunto podem ser aninhadas em uma lista de seleção, embora isto não seja permitido em itens da cláusula FROM. Nesses casos, cada nível de aninhamento é tratado em separado, como se fosse um item LATERAL ROWS FROM( ... ) separado. Por exemplo, em

SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;

as funções retornando conjunto srf2, srf3 e srf5 seriam executadas pari passu com cada linha de tab e, então, srf1 e srf4 seriam aplicadas pari passu com cada linha produzida pelas funções inferiores.

As funções retornando conjunto não podem ser usadas em construções de avaliação condicional, como CASE ou COALESCE. Por exemplo, considere

SELECT x, CASE WHEN x > 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;

Esta expressão pode parecer produzir cinco repetições para as linhas de entrada tendo x > 0, e uma única repetição para aquelas que não têm; mas, na verdade, como generate_series(1, 5) seria executado em um item LATERAL FROM implícito antes da expressão CASE ser avaliada, produziria cinco repetições de toda linha de entrada. Para reduzir a confusão, estes casos produzem um erro de tempo de análise.

Nota

Se o último comando de uma função for INSERT, UPDATE, DELETE ou MERGE com RETURNING, este comando sempre será executado até concluir, mesmo que a função não seja declarada com SETOF, ou a consulta chamadora não busque todas as linhas do resultado. Quaisquer linhas extras produzidas pela cláusula RETURNING são descartadas silenciosamente, mas as modificações de tabela comandada ainda permanecem (e estarão todas concluídas antes da função retornar).

Nota

Antes do PostgreSQL 10, colocar mais de uma função retornando conjunto na mesma lista de seleção não se comportava de forma muito consistente, a menos que sempre produzissem o mesmo número de linhas. Caso contrário, o que se obtinha era um número de linhas de saída igual ao mínimo múltiplo comum dos números de linhas produzidas pelas funções retornando conjunto. Além disso, as funções retornando conjunto aninhadas não funcionavam conforme descrito acima; em vez disso, uma função retornando conjunto poderia ter no máximo um argumento de retorno de conjunto, e cada aninhamento de funções retornando conjunto era executado independentemente. Além disso, a execução condicional (funções retornando conjunto dentro de CASE etc.) era permitida anteriormente, complicando ainda mais as coisas. O uso da sintaxe LATERAL é recomendado ao escrever consultas que precisam funcionar em versões mais antigas do PostgreSQL, porque isto fornecerá resultados consistentes em versões diferentes. Se tiver uma consulta que dependa da execução condicional de uma função retornando conjunto, poderá corrigi-la movendo o teste condicional para uma função retornando conjunto personalizada. Por exemplo,

SELECT x, CASE WHEN y > 0 THEN generate_series(1, z) ELSE 5 END FROM tab;

pode se tornar

CREATE FUNCTION case_generate_series(cond bool, start int, fin int, els int)
  RETURNS SETOF int AS $$
BEGIN
  IF cond THEN
    RETURN QUERY SELECT generate_series(start, fin);
  ELSE
    RETURN QUERY SELECT els;
  END IF;
END$$ LANGUAGE plpgsql;

SELECT x, case_generate_series(y > 0, 1, z, 5) FROM tab;

Esta formulação funciona da mesma forma em todas as versões do PostgreSQL.

36.5.10. Funções SQL retornando TABLE #

Existe outra forma de declarar uma função como retornando um conjunto, que é usar a sintaxe RETURNS TABLE(colunas). Isto equivale a usar um ou mais parâmetros OUT, além de marcar a função como retornando SETOF record (ou SETOF um único tipo de dados de parâmetro de saída, conforme apropriado). Esta notação é especificada em versões recentes do padrão SQL, portanto, pode ser mais portável do que usar SETOF.

Por exemplo, o exemplo anterior de soma e produto também pode ser escrito dessa forma:

CREATE FUNCTION soma_prod_int_com_tab (x int)
RETURNS TABLE(soma int, produto int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

Não é permitido usar parâmetros OUT ou INOUT explícitos com a notação RETURNS TABLE — todas as colunas de saída devem ser colocadas na lista de TABLE.

36.5.11. Funções SQL polimórficas #

As funções SQL podem ser declaradas aceitando e retornando os tipos de dados polimórficos descritos em Tipos de dados polimórficos. A seguir está a função polimórfica make_array que constrói uma matriz de dois elementos de tipo de dados arbitrários:

CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$
    SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;

SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;

 intarray | textarray
----------+-----------
 {1,2}    | {a,b}
(1 linha)

Deve ser observado o uso da conversão de tipo de dados 'a'::text para especificar que o argumento é do tipo de dados text. Isto é necessário se o argumento for apenas um literal cadeia de caracteres, caso contrário, seria tratado como do tipo de dados unknown, e uma matriz de unknown não é um tipo de dados válido. Sem a conversão de tipo de dados, seria mostrado um erro como este:

ERRO:  não foi possível determinar o tipo de dados polimórfico ↵
       porque a entrada tem tipo de dados desconhecido

Com make_array declarado como acima, devem ser fornecidos dois argumentos que sejam exatamente do mesmo tipo de dados; o sistema não tentará resolver quaisquer diferenças de tipo de dados. Assim, por exemplo, isto não funciona:

SELECT make_array(1, 2.5) AS numericarray;
ERRO:  a função make_array(integer, numeric) não existe

Uma abordagem alternativa é usar a família comum de tipos de dados polimórficos, que permite ao sistema tentar identificar um tipo de dados comum adequado:

CREATE FUNCTION make_array2(anycompatible, anycompatible)
RETURNS anycompatiblearray AS $$
    SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;

SELECT make_array2(1, 2.5) AS numericarray;

 numericarray
--------------
 {1,2.5}
(1 linha)

Como as regras para resolução de tipo de dados comum são padronizadas para escolher o tipo de dados text quando todas as entradas são de tipos de dados desconhecidos, a seguinte consulta também funciona:

SELECT make_array2('a', 'b') AS textarray;

 textarray
-----------
 {a,b}
(1 linha)

É permitido ter argumentos polimórficos com o tipo de dados retornado fixo, mas o contrário não é. Por exemplo:

CREATE FUNCTION é_maior(anyelement, anyelement) RETURNS boolean AS $$
    SELECT $1 > $2;
$$ LANGUAGE SQL;

SELECT é_maior(1, 2);

 é_maior
---------
 f
(1 linha)

CREATE FUNCTION func_inválida() RETURNS anyelement AS $$
    SELECT 1;
$$ LANGUAGE SQL;
ERRO:  não é possível determinar o tipo de dados do resultado
DETALHE:  Um resultado do tipo de dados anyelement requer pelo menos uma
          entrada do tipo de dados anyelement, anyarray, anynonarray,
          anyenum, anyrange ou anymultirange.

O polimorfismo pode ser usado com funções que possuem argumentos de saída. Por exemplo:

CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE SQL;

SELECT * FROM dup(22);

 f2 |   f3
----+---------
 22 | {22,22}
(1 linha)

O polimorfismo também pode ser usado com funções variádicas. Por exemplo:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT anyleast(10, -1, 5, 4);

 anyleast
----------
       -1
(1 linha)

SELECT anyleast('abc'::text, 'def');

 anyleast
----------
 abc
(1 linha)

CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$
    SELECT array_to_string($2, $1);
$$ LANGUAGE SQL;

SELECT concat_values('|', 1, 4, 2);

 concat_values
---------------
 1|4|2
(1 linha)

36.5.12. Funções SQL com ordenação #

Quando uma função SQL tem um ou mais parâmetros de tipos de dados ordenáveis, é identificada uma ordenação para cada chamada da função, dependendo das ordenações atribuídas aos argumentos reais, conforme descrito na Suporte a ordenação. Se uma ordenação for identificada com sucesso (ou seja, não há conflitos de ordenações implícitas entre os argumentos) então, todos os parâmetros que podem ser ordenados são tratados como tendo esta ordenação implicitamente. Isto afeta o comportamento das operações sensíveis à ordenação dentro da função. Por exemplo, usando a função anyleast descrita acima, o resultado de

SELECT anyleast('abc'::text, 'ABC');

é dependente da ordenação padrão do banco de dados. Na localidade C o resultado será ABC, mas em muitas outras localidades será abc. A ordenação a ser usada pode ser forçada adicionando a cláusula COLLATE a qualquer um dos argumentos. Por exemplo

SELECT anyleast('abc'::text, 'ABC' COLLATE "C");

Como alternativa, se for desejado que a função opere com uma ordenação específica, independentemente de como ela é chamada, devem ser inseridas cláusulas COLLATE conforme necessário na definição da função. A versão de anyleast a seguir sempre usa a localidade en_US para comparar cadeias de caracteres:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

Mas veja que isto gera um erro quando aplicado a um tipo de dados não ordenável.

Se não puder ser identificada nenhuma ordenação comum entre os argumentos reais, a função SQL tratará seus parâmetros como tendo a ordenação padrão de seus tipos de dados (que é geralmente a ordenação padrão do banco de dados, mas pode ser diferente para parâmetros de tipos de dados de domínio).

O comportamento de parâmetros ordenáveis pode ser pensado como uma forma limitada de polimorfismo, aplicável apenas a tipos de dados de texto.