43.1. Funções e argumentos no PL/Perl #

As funções escritas na linguagem PL/Perl, são definidas por meio da sintaxe de CREATE FUNCTION padrão:

CREATE FUNCTION nome_da_função (tipos_de_dados_dos_argumentos)
RETURNS tipo_retornado
-- os atributos de função podem vir aqui
AS $$
    # o corpo da função PL/Perl vem aqui
$$ LANGUAGE plperl;

O corpo da função é um código Perl comum. Na verdade, o código PL/Perl adicional o envolve numa sub-rotina Perl. A função PL/Perl é chamada em um contexto escalar, então ela não pode retornar uma lista. Podem ser retornados valores não escalares (matrizes, registros e conjuntos) retornando uma referência, conforme discutido abaixo.

Em um procedimento PL/Perl, qualquer valor retornado pelo código Perl é ignorado.

O PL/Perl também oferece suporte a blocos de código anônimos, chamados com a instrução DO:

DO $$
    # código PL/Perl
$$ LANGUAGE plperl;

Os blocos de código anônimos não recebem argumentos, e qualquer valor que possa ser retornado será descartado. Fora isto, se comporta como uma função.

Nota

O uso de sub-rotinas com nome aninhadas é perigoso em Perl, especialmente se elas se referirem a variáveis ​​lexicais no escopo envoltório. Como a função PL/Perl é envolta em uma sub-rotina, qualquer sub-rotina com nome que se colocar dentro será aninhada. Em geral, é muito mais seguro criar sub-rotinas anônimas chamadas por meio de uma referência. Para obter mais informações, veja as entradas para A variável "%s" não permanecerá compartilhada e A variável "%s" não está disponível na página do manual perldiag, ou procure na Internet por sub-rotina com nome aninhada em perl.

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 optado por usar a sintaxe de cadeia de caracteres com escape E'', devem ser duplicados quaisquer apóstrofos (') e contrabarras (\) presentes no corpo da função (veja Constantes do tipo cadeia de caracteres).

Os argumentos e resultados são tratados como em qualquer outra sub-rotina Perl: os argumentos são passados ​por @_, e o valor do resultado é retornado por return, ou como a última expressão avaliada na função.

Por exemplo, uma função para retornar o maior valor entre dois números inteiros pode ser definida como:

CREATE OR REPLACE FUNCTION perl_max (integer, integer)
  RETURNS integer
AS $$
    if ($_[0] > $_[1]) { return $_[0]; }
    return $_[1];
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT perl_max(-5, -20);

 perl_max
----------
       -5
(1 linha)

Nota

Os argumentos são convertidos da codificação do banco de dados para UTF-8 para uso dentro de PL/Perl e, em seguida, convertidos de UTF-8 de volta para a codificação do banco de dados no retorno.

Se um valor nulo do SQL for passado para uma função, o valor do argumento aparecerá como indefinido no Perl. A definição da função acima não se comportará muito bem com entradas nulas (na verdade, irá funcionar como se fossem zeros). Poderia ser adicionado STRICT à definição da função para fazer com que o PostgreSQL faça algo mais razoável: se for passado um valor nulo, a função não será chamada, apenas retornando um resultado nulo automaticamente. Como alternativa, pode-se verificar se existem entradas nulas no corpo da função: por exemplo, supondo que se queira que perl_max, ao receber um argumento nulo e outro não nulo, retorne o argumento não nulo, em vez do valor nulo:

CREATE OR REPLACE FUNCTION perl_max (integer, integer)
  RETURNS integer
AS $$
    my ($x, $y) = @_;
    if (not defined $x) {
        return undef if not defined $y;
        return $y;
    }
    return $x if not defined $y;
    return $x if $x > $y;
    return $y;
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT perl_max(NULL, -20);

 perl_max
----------
      -20
(1 linha)

Como mostrado acima, para retornar o valor nulo do SQL de uma função escrita em PL/Perl, deve ser retornado o valor indefinido. Isto pode ser feito independentemente da função ser estrita ou não.

Qualquer coisa em um argumento de função que não seja uma referência é uma cadeia de caracteres, que está na representação de texto externo padrão do PostgreSQL para o tipo de dados relevante. No caso de tipos numéricos ou de texto comuns, o Perl fará a coisa certa e o programador normalmente não terá que se preocupar com isto. Entretanto, em outros casos, o argumento precisará ser convertido para uma forma mais utilizável no Perl. Por exemplo, a função decode_bytea pode ser usada para converter um argumento do tipo de dados bytea em binário sem escape.

De forma semelhante, os valores passados ​​de volta para o PostgreSQL devem estar no formato de representação de texto externo. Por exemplo, a função encode_bytea pode ser usada para realizar escape de dados binários para um valor retornado do tipo de dados bytea.

Um caso particularmente importante são os valores booleanos. Como dito antes, o comportamento padrão para valores bool é que eles são passados ​​para o Perl como texto, portanto, 't' ou 'f'. Isto é problemático, porque o Perl não trata 'f' como falso! É possível melhorar as coisas usando uma transformação (veja CREATE TRANSFORM). Transformações adequadas são fornecidas pela extensão bool_plperl. Para usá-las, a extensão deverá ser instalada:

CREATE EXTENSION bool_plperl;  -- ou bool_plperlu para o PL/PerlU

Em seguida, deve ser usado o atributo de função TRANSFORM para uma função PL/Perl, que recebe ou retorna bool. Por exemplo:

CREATE OR REPLACE FUNCTION perl_and(bool, bool) RETURNS bool
TRANSFORM FOR TYPE bool
AS $$
  my ($a, $b) = @_;
  return $a && $b;
$$ LANGUAGE plperl;

Quando esta transformação for aplicada, os argumentos bool serão vistos pelo Perl como sendo 1 ou vazios, portanto verdade ou falso. Se o resultado da função for do tipo bool, será verdade ou falso, dependendo se o Perl avaliar o valor retornado como verdade, ou não. Transformações semelhantes também são executadas para argumentos de consulta booleana e resultados de consultas SPI realizadas dentro da função (veja Acesso a banco de dados no PL/Perl).

Perl pode retornar matrizes do PostgreSQL como referências a matrizes Perl. A seguir está um exemplo:

CREATE OR REPLACE function retorna_matriz()
RETURNS text[][] AS $$
    return [['a"b','c,d'],['e\\f','g']];
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT retorna_matriz();

       retorna_matriz
-----------------------------
 {{"a\"b","c,d"},{"e\\f",g}}
(1 linha)

O Perl usa as matrizes do PostgreSQL como um objeto PostgreSQL::InServer::ARRAY referenciado. Este objeto será tratado como uma referência de matriz, ou como uma cadeia de caracteres, permitindo a compatibilidade com o código Perl escrito para as versões do PostgreSQL anteriores a 9.1. Por exemplo:

CREATE OR REPLACE FUNCTION concat_elementos_matriz(text[]) RETURNS TEXT AS $$
    my $arg = shift;
    my $result = "";
    return undef if (!defined $arg);

    # como uma referência de matriz
    for (@$arg) {
        $result .= $_;
    }

    # também funciona como uma cadeia de caracteres
    $result .= $arg;

    return $result;
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT concat_elementos_matriz(ARRAY['PL','/','Perl']);

 concat_elementos_matriz
-------------------------
 PL/Perl{PL,/,Perl}
(1 linha)

Nota

As matrizes multidimensionais são representadas como referências a matrizes de referência de menor dimensão, de uma forma comum a todo programador Perl.

Os argumentos do tipo de dados composto são passados ​​para a função como referências a hashes [130]. As chaves do hash são os nomes dos atributos do tipo de dados composto. A seguir está um exemplo:

CREATE TABLE empregado (
    nome text,
    salario_base integer,
    bonus integer
);
CREATE TABLE
CREATE OR REPLACE FUNCTION emp_sal_bonus(empregado)
  RETURNS integer
AS $$
    my ($emp) = @_;
    return $emp->{salario_base} + $emp->{bonus};
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT nome, emp_sal_bonus(empregado.*) FROM empregado;

Uma função escrita em PL/Perl pode retornar um resultado do tipo composto usando a mesma abordagem: retornando uma referência a um hash que tenha os atributos necessários. Por exemplo:

CREATE TYPE testa_linha_perl AS (f1 integer, f2 text, f3 text);
CREATE TYPE
CREATE OR REPLACE FUNCTION linha_perl()
  RETURNS testa_linha_perl
AS $$
    return {f2 => 'hello', f1 => 1, f3 => 'world'};
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT * FROM linha_perl();

 f1 |  f2   |  f3
----+-------+-------
  1 | hello | world
(1 linha)

Qualquer coluna no tipo de dados declarado do resultado, que não esteja presente no hash, será retornada como NULL.

Da mesma forma, os argumentos de saída dos procedimentos podem ser retornados no formato de referência a hash:

CREATE OR REPLACE PROCEDURE perl_triplo(INOUT a integer, INOUT b integer)
AS $$
    my ($a, $b) = @_;
    return {a => $a * 3, b => $b * 3};
$$ LANGUAGE plperl;
CREATE PROCEDURE
CALL perl_triplo(5, 10);

 a  | b
----+----
 15 | 30
(1 linha)

As funções escritas em PL/Perl também podem retornar conjuntos de tipos de dados escalares ou compostos. Normalmente, se deseja retornar as linhas uma de cada vez, tanto para acelerar o tempo de início, quanto para evitar enfileirar todo o conjunto de resultados na memória. Isto pode ser feito usando return_next, como mostrado abaixo. Note que após o último return_next, deve ser colocado return, ou (melhor ainda) return undef.

CREATE OR REPLACE FUNCTION perl_conjunto_inteiros(int)
  RETURNS SETOF INTEGER
AS $$
    foreach (0..$_[0]) {
        return_next($_);
    }
    return undef;
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT * FROM perl_conjunto_inteiros(3);

 perl_conjunto_inteiros
------------------------
                      0
                      1
                      2
                      3
(4 linhas)

CREATE OR REPLACE FUNCTION perl_conjunto()
  RETURNS SETOF testa_linha_perl
AS $$
    return_next({ f1 => 1, f2 => 'Hello', f3 => 'World' });
    return_next({ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' });
    return_next({ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' });
    return undef;
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT perl_conjunto();

    perl_conjunto
----------------------
 (1,Hello,World)
 (2,Hello,PostgreSQL)
 (3,Hello,PL/Perl)
(3 linhas)

Para pequenos conjuntos de resultados, pode-se retornar uma referência a uma matriz contendo escalares, referências a matrizes, ou referências hash para tipos de dados simples, tipos de dados de matriz, e tipos de dados compostos, respectivamente. A seguir estão alguns exemplos simples de como retornar todo o conjunto de resultados como uma referência de matriz:

CREATE OR REPLACE FUNCTION perl_conjunto_inteiros(int)
  RETURNS SETOF INTEGER
AS $$
    return [0..$_[0]];
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT * FROM perl_conjunto_inteiros(3);

 perl_conjunto_inteiros
------------------------
                      0
                      1
                      2
                      3
(4 linhas)

CREATE OR REPLACE FUNCTION perl_conjunto()
  RETURNS SETOF testa_linha_perl
AS $$
    return [
        { f1 => 1, f2 => 'Hello', f3 => 'World' },
        { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
        { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
    ];
$$ LANGUAGE plperl;
CREATE FUNCTION
SELECT * FROM perl_conjunto();

 f1 |  f2   |     f3
----+-------+------------
  1 | Hello | World
  2 | Hello | PostgreSQL
  3 | Hello | PL/Perl
(3 linhas)

Se for desejado usar o pragma strict no código, existem algumas opções. Para uso global temporário, pode-se declarar SET plperl.use_strict como verdade. Isto irá afetar as compilações posteriores das funções escritas em PL/Perl, mas não funções já compiladas na sessão corrente. Para uso global permanente, pode-se definir plperl.use_strict como verdade no arquivo postgresql.conf.

Para uso permanente em funções específicas, pode-se simplesmente colocar:

use strict;

no topo do corpo da função.

A feature pragma também estará disponível na função use, se o Perl usado for versão 5.10.0 ou superior.



[130] O hash do Perl é definido por pares chave-valor. O Perl armazena os elementos de um hash de uma maneira tão ideal, que se pode procurar seus valores com base em chaves muito rapidamente. Perl Hash (N. T.)