As funções escritas na linguagem PL/Perl, são definidas por meio da sintaxe de CREATE FUNCTION padrão:
CREATE FUNCTIONnome_da_função(tipos_de_dados_dos_argumentos) RETURNStipo_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.
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)
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)
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.