Para crear una función en el lenguaje PL/Perl, utiliza la sintaxis estándar de CREATE FUNCTION:
CREATE FUNCTIONfuncname(argument-types) RETURNSreturn-type-- los atributos de la función pueden ir aquí AS $$ # el cuerpo de la función PL/Perl va aquí $$ LANGUAGE plperl;
El cuerpo de la función es código Perl ordinario. De hecho, el código de acoplamiento de PL/Perl lo envuelve dentro de una subrutina Perl. Una función PL/Perl se llama en un contexto escalar, por lo que no puede devolver una lista. Puedes devolver valores no escalares (arrays, registros y conjuntos) devolviendo una referencia, como se explica a continuación.
En un procedimiento PL/Perl, cualquier valor de retorno del código Perl es ignorado.
PL/Perl también admite bloques de código anónimos llamados con la sentencia DO:
DO $$
# código PL/Perl
$$ LANGUAGE plperl;
Un bloque de código anónimo no recibe argumentos, y cualquier valor que pueda devolver es descartado. Por lo demás, se comporta igual que una función.
El uso de subrutinas anidadas nombradas es peligroso en Perl, especialmente si
hacen referencia a variables léxicas en el ámbito contenedor. Debido a que una función
PL/Perl está envuelta en una subrutina, cualquier subrutina nombrada que coloques dentro
de ella estará anidada. En general, es mucho más seguro crear subrutinas
anónimas que llames a través de un coderef. Para obtener más información, consulta las
entradas para Variable "%s" will not stay shared y
Variable "%s" is not available en la página de manual de
perldiag, o
busca en Internet “perl nested named subroutine”.
La sintaxis del comando CREATE FUNCTION requiere
que el cuerpo de la función se escriba como una constante de cadena. Normalmente
es más conveniente utilizar el entrecomillado de dólar (dollar quoting) (consulta la Section 4.1.2.4) para la constante de cadena.
Si decides utilizar la sintaxis de cadena de escape E'',
debes duplicar las comillas simples (') y barras invertidas
(\) utilizadas en el cuerpo de la función
(consulta la Section 4.1.2.1).
Los argumentos y resultados se manejan como en cualquier otra subrutina de Perl:
los argumentos se pasan en @_, y el valor del resultado
se devuelve con return or como la última expresión
evaluada en la función.
Por ejemplo, una función que devuelva el mayor de dos valores enteros podría definirse como:
CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
if ($_[0] > $_[1]) { return $_[0]; }
return $_[1];
$$ LANGUAGE plperl;
Los argumentos se convertirán de la codificación de la base de datos a UTF-8 para su uso dentro de PL/Perl, y luego se convertirán de UTF-8 de regreso a la codificación de la base de datos al retornar.
Si se pasa un valor nulo de SQL a una función,
el valor del argumento aparecerá como “indefinido” (undefined) en Perl. La
definición de la función anterior no se comportará muy bien con entradas nulas
(de hecho, actuará como si fueran ceros). Podríamos
añadir STRICT a la definición de la función para hacer que
PostgreSQL haga algo más razonable:
si se pasa un valor nulo, la función no se llamará en absoluto,
sino que simplemente devolverá un resultado nulo automáticamente. Alternativamente,
podríamos comprobar las entradas no definidas en el cuerpo de la función. Por
ejemplo, supongamos que quisiéramos que perl_max con
un argumento nulo y otro no nulo devolviera el argumento no nulo,
en lugar de un valor nulo:
CREATE 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;
Como se muestra arriba, para devolver un valor nulo de SQL desde una función PL/Perl, devuelve un valor indefinido. Esto se puede hacer tanto si la función es estricta (strict) como si no lo es.
Cualquier cosa en un argumento de función que no sea una referencia es
una cadena, la cual está en la representación de texto externa estándar de
PostgreSQL para el tipo de datos correspondiente. En el caso de
los tipos numéricos o de texto ordinarios, Perl simplemente hará lo correcto y
el programador normalmente no tendrá que preocuparse por ello. Sin embargo, en
otros casos el argumento necesitará ser convertido a una forma que sea
más utilizable en Perl. Por ejemplo, la función decode_bytea
se puede utilizar para convertir un argumento de
tipo bytea en binario sin escapar.
De manera similar, los valores devueltos a PostgreSQL
deben estar en el formato de representación de texto externa. Por ejemplo, la
función encode_bytea se puede utilizar para
escapar datos binarios para un valor de retorno de tipo bytea.
Un caso que es particularmente importante son los valores booleanos. Como se acaba de
mencionar, el comportamiento por defecto para los valores bool es que
se pasan a Perl como texto, por lo tanto, ya sea 't'
o 'f'. Esto es problemático, ya que Perl no
tratará 'f' como falso. Es posible mejorar esto
utilizando una “transformación” (transform) (consulta la
CREATE TRANSFORM). Las transformaciones adecuadas son proporcionadas
por la extensión bool_plperl. Para usarla, instala
la extensión:
CREATE EXTENSION bool_plperl; -- o bool_plperlu para PL/PerlU
Luego, utiliza el atributo de función TRANSFORM para una
función PL/Perl que reciba o devuelva bool, por ejemplo:
CREATE FUNCTION perl_and(bool, bool) RETURNS bool TRANSFORM FOR TYPE bool AS $$ my ($a, $b) = @_; return $a && $b; $$ LANGUAGE plperl;
Cuando se aplica esta transformación, los argumentos bool serán vistos
por Perl como 1 o vacío, es decir, propiamente verdaderos o
falsos. Si el resultado de la función es de tipo bool, será verdadero
o falso según si Perl evaluaría el valor devuelto como
verdadero.
Transformaciones similares también se realizan para los argumentos de consulta booleanos
y los resultados de las consultas SPI realizadas dentro de la función
(Section 43.3.1).
Perl puede devolver arrays de PostgreSQL como referencias a arrays de Perl. Aquí hay un ejemplo:
CREATE OR REPLACE function returns_array()
RETURNS text[][] AS $$
return [['a"b','c,d'],['e\\f','g']];
$$ LANGUAGE plperl;
select returns_array();
Perl pasa los arrays de PostgreSQL como un objeto
bendecido (blessed) PostgreSQL::InServer::ARRAY. Este objeto puede ser tratado como una
referencia a un array o como una cadena, lo que permite la compatibilidad hacia atrás con el código Perl
escrito para versiones de PostgreSQL inferiores a la 9.1.
Por ejemplo:
CREATE OR REPLACE FUNCTION concat_array_elements(text[]) RETURNS TEXT AS $$
my $arg = shift;
my $result = "";
return undef if (!defined $arg);
# como una referencia a un array
for (@$arg) {
$result .= $_;
}
# también funciona como una cadena
$result .= $arg;
return $result;
$$ LANGUAGE plperl;
SELECT concat_array_elements(ARRAY['PL','/','Perl']);
Los arrays multidimensionales se representan como referencias a arrays de referencias de menor dimensión de una manera común para cualquier programador de Perl.
Los argumentos de tipo compuesto se pasan a la función como referencias a hashes. Las claves del hash son los nombres de los atributos del tipo compuesto. Aquí hay un ejemplo:
CREATE TABLE employee (
name text,
basesalary integer,
bonus integer
);
CREATE FUNCTION empcomp(employee) RETURNS integer AS $$
my ($emp) = @_;
return $emp->{basesalary} + $emp->{bonus};
$$ LANGUAGE plperl;
SELECT name, empcomp(employee.*) FROM employee;
Una función PL/Perl puede devolver un resultado de tipo compuesto utilizando el mismo enfoque: devolver una referencia a un hash que tenga los atributos requeridos. Por ejemplo:
CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text);
CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$
return {f2 => 'hello', f1 => 1, f3 => 'world'};
$$ LANGUAGE plperl;
SELECT * FROM perl_row();
Cualquier columna en el tipo de datos de resultado declarado que no esté presente en el hash se devolverá como valor nulo.
De manera similar, los argumentos de salida de los procedimientos se pueden devolver como una referencia a un hash:
CREATE PROCEDURE perl_triple(INOUT a integer, INOUT b integer) AS $$
my ($a, $b) = @_;
return {a => $a * 3, b => $b * 3};
$$ LANGUAGE plperl;
CALL perl_triple(5, 10);
Las funciones PL/Perl también pueden devolver conjuntos (sets) de tipos escalares o
compuestos. Normalmente querrás devolver las filas una a la
vez, tanto para acelerar el tiempo de inicio como para evitar acumular
todo el conjunto de resultados en memoria. Puedes hacer esto con
return_next como se ilustra a continuación. Ten en cuenta que
después del último return_next, debes poner
ya sea return o (mejor) return
undef.
CREATE OR REPLACE FUNCTION perl_set_int(int)
RETURNS SETOF INTEGER AS $$
foreach (0..$_[0]) {
return_next($_);
}
return undef;
$$ LANGUAGE plperl;
SELECT * FROM perl_set_int(5);
CREATE OR REPLACE FUNCTION perl_set()
RETURNS SETOF testrowperl 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;
Para conjuntos de resultados pequeños, puedes devolver una referencia a un array que contenga escalares, referencias a arrays o referencias a hashes para tipos simples, tipos de array y tipos compuestos, respectivamente. Aquí hay algunos ejemplos sencillos de devolución de todo el conjunto de resultados como una referencia a un array:
CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$
return [0..$_[0]];
$$ LANGUAGE plperl;
SELECT * FROM perl_set_int(5);
CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$
return [
{ f1 => 1, f2 => 'Hello', f3 => 'World' },
{ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' },
{ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' }
];
$$ LANGUAGE plperl;
SELECT * FROM perl_set();
Si deseas utilizar la directiva (pragma) strict con tu código,
tienes algunas opciones. Para uso global temporal puedes ejecutar SET
plperl.use_strict en true.
Esto afectará a las compilaciones posteriores de funciones PL/Perl,
pero no a las funciones ya compiladas en la sesión actual.
Para uso global permanente puedes establecer plperl.use_strict
en true en el archivo postgresql.conf.
Para un uso permanente en funciones específicas, simplemente puedes poner:
use strict;
en la parte superior del cuerpo de la función.
La directiva feature también está disponible para use si tu versión de Perl es 5.10.0 o superior.