10.3. Funções #

A função específica referenciada por uma chamada de função é determinada usando o procedimento a seguir.

Resolução do tipo de dados da função

  1. Selecione as funções a serem consideradas no catálogo do sistema pg_proc. Se foi usado um nome de função não qualificado pelo esquema, as funções consideradas são aquelas com o nome e número de argumentos correspondentes visíveis no caminho de procura corrente (veja a Seção 5.10.3). Se foi fornecido um nome de função qualificado, serão consideradas somente as funções no esquema especificado.

    1. Se forem encontradas várias funções com argumentos de tipos de dados idênticos no caminho de procura, apenas a função que aparecer primeiro no caminho será considerada. Funções com argumentos de tipos de dados diferentes são consideradas em pé de igualdade, independentemente da posição no caminho de procura.

    2. Se uma função for declarada como parâmetro de matriz VARIADIC, e a chamada não usar a palavra-chave VARIADIC, então a função será tratada como se o parâmetro da matriz fosse substituído por uma ou mais ocorrências de seu tipo de dados de elemento, conforme necessário, para corresponder à chamada. Após esta expansão, a função poderá ter argumentos do mesmo tipo de dados de alguma função não VARIADIC. Neste caso, a função que aparecer primeiro no caminho de procura será usada ou, se as duas funções estiverem no mesmo esquema, a função não VARIADIC será a preferida.

      A falta de uma correspondência exata cria um risco de segurança ao se chamar, por meio de um nome qualificado [93], uma função VARIADIC encontrada em um esquema que permite que usuários não confiáveis criem objetos. Um usuário mal-intencionado pode assumir o controle e executar funções SQL arbitrárias como se você as tivesse executando. Substitua por uma chamada usando a palavra-chave VARIADIC, para evitar este risco. As chamadas que preenchem os parâmetros VARIADIC "any" geralmente não têm formulação equivalente contendo a palavra-chave VARIADIC. Para executar estas chamadas com segurança, o esquema da função deve permitir que apenas usuários confiáveis criem objetos.

    3. As funções que possuem valor padrão para parâmetros são consideradas para corresponder a qualquer chamada que omita zero ou mais posições de parâmetro padrão. Se mais de uma dessas funções corresponder a chamada, será usada a que aparecer primeiro no caminho de procura. Se houver duas ou mais destas funções no mesmo esquema com parâmetros de tipos de dados de idênticos nas posições não-padrão (o que é possível se tiverem diferentes conjuntos de parâmetros padrão), o sistema não conseguirá determinar qual delas prefere, e assim resultará em erro de chamada ambígua de função se nenhuma correspondência melhor para a chamada puder ser encontrada.

      Isto cria um risco de disponibilidade ao se chamar, por meio de nome qualificado [93], qualquer função encontrada em um esquema que permita que usuários não confiáveis criem objetos. Um usuário mal-intencionado pode criar uma função com o nome de uma função existente, replicando os parâmetros desta função e anexando novos parâmetros com valores padrão. Isto impede novas chamadas para a função original. Para evitar este perigo, coloque funções em esquemas que permitam que apenas usuários confiáveis criem objetos.

  2. Verifique se há uma função que aceita exatamente os mesmos tipos de dados dos argumentos de entrada. Se existir uma (só pode haver uma correspondência exata no conjunto de funções considerado), use-a. A falta de correspondência exata cria um risco de segurança ao se chamar, por meio de nome qualificado [93], uma função que se encontra em um esquema que permite usuários não confiáveis criarem objetos. Em tais situações, os argumentos devem ser convertidos para forçar uma correspondência exata. (Os casos envolvendo o tipo de dados unknown nunca vão encontrar uma correspondência nessa etapa.)

  3. Se não for encontrada nenhuma correspondência exata, veja se a chamada da função parece ser uma solicitação de conversão de tipo de dados especial. Isto acontece se a chamada da função tiver apenas um argumento, e o nome da função for idêntico ao nome (interno) de algum tipo de dados. Além disso, o argumento da função deve ser um literal de tipo de dados desconhecido, ou um tipo de dados que seja binariamente-conversível para o tipo de dados indicado, ou um tipo de dados que possa ser convertido para o tipo de dados indicado aplicando as funções de E/S deste tipo de dados (ou seja, a conversão é de, ou para, um dos tipos de dados cadeia de caracteres padrão). Quando estas condições são atendidas, a chamada da função é tratada como uma forma de especificação de CAST. [94]

  4. Procure a melhor correspondência.

    1. Descarte as funções candidatas para as quais os tipos de dados de entrada não correspondem, e não podem ser convertidos (usando uma conversão implícita) para corresponder. Literais do tipo unknown são considerados conversíveis para qualquer tipo de dados para este propósito Se restar apenas uma candidata, use-a; senão, siga para a próxima etapa.

    2. Se algum argumento de entrada for de um tipo de dados de domínio, trate-o como sendo do tipo de dados base do domínio para todas as etapas subsequentes. Isto garante que os domínios ajam como seus tipos de dados base para fins de resolução de função ambígua.

    3. Percorra todos as candidatas, e mantenha aquelas com as correspondências mais exatas nos tipos de dados de entrada. Mantenha todas as candidatas, se nenhuma tiver correspondências exatas. Se restar apenas uma candidata, use-a; senão, siga para a próxima etapa.

    4. Percorra todas as candidatas e mantenha aquelas que aceitam os tipos de dados preferidos (da categoria de tipo de dados do tipo de dados de entrada) na maioria das posições onde a conversão de tipo de dados será necessária. Mantenha todas as candidatas se nenhuma aceitar os tipos de dados preferidos. Se restar apenas uma candidata, use-a; senão, siga para a próxima etapa.

    5. Se algum argumento de entrada for do tipo de dados unknown, verifique as categorias de tipo de dados aceitas nestas posições de argumento pelas candidatas restantes. Em cada posição, selecione a categoria string, se alguma candidata aceitar esta categoria. (Este viés para cadeia de caracteres é apropriado, porque um literal de tipo de dados desconhecido se parece com uma cadeia de caracteres.) Senão, se todos as candidatas restantes aceitarem a mesma categoria de tipo de dados, selecione esta categoria; senão falhe, porque a escolha correta não pode ser deduzida sem mais pistas. Agora descarte as candidatas que não aceitam a categoria de tipo de dados selecionada. Além disso, se alguma candidata aceitar um tipo de dados preferido nesta categoria, descarte as candidatas que aceitam tipos de dados não preferidos para esse argumento. Mantenha todas as candidatas se nenhuma sobreviver a esses testes. Se restar apenas uma candidata, use-a; senão, siga para a próxima etapa.

    6. Se houver argumentos do tipo de dados unknown e de tipo de dados conhecido, e todos os argumentos de tipo de dados conhecido tiverem o mesmo tipo de dados, suponha que os argumentos unknown também sejam deste tipo de dados, e verifique quais candidatas podem aceitar este tipo de dados nas posições do argumento unknown. Se restar apenas uma candidata, use-a. Senão, falhe.

Note que as regras de melhor correspondência são idênticas para resolução de tipo de dados de operador e de função. Seguem alguns exemplos.

Exemplo 10.6. Resolução do tipo de dados da função de arredondamento

Existe apenas uma função round que aceita dois argumentos; a função aceita um primeiro argumento do tipo de dados numeric e um segundo argumento do tipo de dados integer. Portanto, a consulta a seguir converte automaticamente o primeiro argumento do tipo de dados integer para o tipo de dados numeric:

SELECT round(4, 4);

 round
--------
 4.0000
(1 linha)

Na realidade, esta consulta é transformada pelo analisador em:

SELECT round(CAST (4 AS numeric), 4);

Como as constantes numéricas com pontos decimais têm inicialmente atribuído o tipo de dados numeric, a consulta a seguir não exigirá conversão de tipo de dados, portanto pode ser um pouco mais eficiente:

SELECT round(4.0, 4);


Exemplo 10.7. Resolução do tipo de dados de função VARIADIC

CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int
  LANGUAGE sql AS 'SELECT 1';
CREATE FUNCTION

Essa função aceita, mas não requer, a palavra-chave VARIADIC. Ela tolera argumentos inteiros e numéricos:

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);

 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                1 |                1 |                1
(1 linha)

No entanto, a primeira e a segunda chamadas vão preferir funções mais específicas, se estiverem disponíveis:

CREATE FUNCTION public.variadic_example(numeric) RETURNS int
  LANGUAGE sql AS 'SELECT 2';
CREATE FUNCTION

CREATE FUNCTION public.variadic_example(int) RETURNS int
  LANGUAGE sql AS 'SELECT 3';
CREATE FUNCTION

SELECT public.variadic_example(0),
       public.variadic_example(0.0),
       public.variadic_example(VARIADIC array[0.0]);

 variadic_example | variadic_example | variadic_example
------------------+------------------+------------------
                3 |                2 |                1
(1 linha)

Dada a configuração padrão e existindo apenas a primeira função, a primeira e a segunda chamadas são inseguras. Qualquer usuário poderia interceptá-las criando a segunda ou a terceira função. Correspondendo exatamente ao tipo de dados do argumento e usando a palavra-chave VARIADIC, a terceira chamada é segura.


Exemplo 10.8. Resolução do tipo de dados da função substr

Existem várias funções substr, uma das quais aceita os tipos de dados text e integer. Se chamada com uma constante cadeia de caracteres de tipo de dados não especificado, o sistema escolherá a função candidata que aceita um argumento da categoria preferida string (que é do tipo de dados text).

SELECT substr('1234', 3);

 substr
--------
     34
(1 linha)

Se a cadeia de caracteres for declarada tendo o tipo de dados varchar, que pode ser o caso se vier de uma tabela, o analisador tentará convertê-la para text:

SELECT substr(varchar '1234', 3);

 substr
--------
     34
(1 linha)

Esta chamada é transformada pelo analisador para se tornar de fato:

SELECT substr(CAST (varchar '1234' AS text), 3);

Nota

O analisador aprende com o catálogo pg_cast que os tipos de dados text e varchar são compatíveis binariamente, significando que um pode ser passado para uma função que aceita o outro sem fazer qualquer conversão física. Portanto, na verdade, nenhuma chamada de conversão de tipo de dados é inserida neste caso.

E, se a função for chamada com um argumento do tipo de dados integer, o analisador tentará convertê-lo para text:

SELECT substr(1234, 3);
ERRO:  não existe a função substr(integer, integer)
LINHA 1: SELECT substr(1234, 3);
                ^
DICA:  Nenhuma função corresponde com o nome e tipos de dados dos argumentos informados.
       Você precisa adicionar conversões de tipo de dados explícitas.

Esta chamada não funciona, porque o tipo de dados integer não tem uma conversão implícita para o tipo de dados text. Entretanto, uma conversão explícita irá funcionar:

SELECT substr(CAST (1234 AS text), 3);

 substr
--------
     34
(1 linha)




[93] O perigo não surge com nome não qualificado pelo esquema, porque um caminho de procura contendo esquemas que permitem usuários não confiáveis criarem objetos não é um padrão de uso de esquema seguro.

[94] A razão para esta etapa existir, é oferecer suporte a especificações de conversão no estilo função, nos casos onde não existe atualmente uma função de conversão. Se houver uma função de conversão, ela receberá um nome convencional segundo seu tipo de dados de saída, portanto não há necessidade de um caso especial. Veja CREATE CAST para obter comentário adicional.