43.3. Funciones incorporadas #

43.3.1. Acceso a la base de datos desde PL/Perl
43.3.2. Funciones de utilidad en PL/Perl

43.3.1. Acceso a la base de datos desde PL/Perl #

El acceso a la base de datos desde tu función Perl se puede realizar a través de las siguientes funciones:

spi_exec_query(query [, limit])

spi_exec_query ejecuta un comando SQL y devuelve todo el conjunto de filas como una referencia a un array de referencias a hashes. Si se especifica limit y es mayor que cero, entonces spi_exec_query recupera como máximo limit filas, de la misma manera que si la consulta incluyera una cláusula LIMIT. Omitir limit o especificarlo como cero da como resultado que no haya límite de filas.

Solo debes usar este comando cuando sepas que el conjunto de resultados será relativamente pequeño. Aquí hay un ejemplo de una consulta (comando SELECT) con el número máximo opcional de filas:

$rv = spi_exec_query('SELECT * FROM my_table', 5);

Esto devuelve hasta 5 filas de la tabla my_table. Si my_table tiene una columna my_column, puedes obtener ese valor de la fila $i del resultado de esta manera:

$foo = $rv->{rows}[$i]->{my_column};

Se puede acceder al número total de filas devueltas por una consulta SELECT de esta manera:

$nrows = $rv->{processed}

Aquí hay un ejemplo que utiliza un tipo de comando diferente:

$query = "INSERT INTO my_table VALUES (1, 'test')";
$rv = spi_exec_query($query);

Luego puedes acceder al estado del comando (por ejemplo, SPI_OK_INSERT) de esta manera:

$res = $rv->{status};

Para obtener el número de filas afectadas, haz lo siguiente:

$nrows = $rv->{processed};

Aquí hay un ejemplo completo:

CREATE TABLE test (
    i int,
    v varchar
);

INSERT INTO test (i, v) VALUES (1, 'first line');
INSERT INTO test (i, v) VALUES (2, 'second line');
INSERT INTO test (i, v) VALUES (3, 'third line');
INSERT INTO test (i, v) VALUES (4, 'immortal');

CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$
    my $rv = spi_exec_query('select i, v from test;');
    my $status = $rv->{status};
    my $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        my $row = $rv->{rows}[$rn];
        $row->{i} += 200 if defined($row->{i});
        $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
        return_next($row);
    }
    return undef;
$$ LANGUAGE plperl;

SELECT * FROM test_munge();

spi_query(command)
spi_fetchrow(cursor)
spi_cursor_close(cursor)

spi_query y spi_fetchrow trabajan juntos como una pareja para conjuntos de filas que podrían ser grandes, o para casos en los que deseas devolver filas a medida que llegan. spi_fetchrow funciona solo con spi_query. El siguiente ejemplo ilustra cómo los usas juntos:

CREATE TYPE foo_type AS (the_num INTEGER, the_text TEXT);

CREATE OR REPLACE FUNCTION lotsa_md5 (INTEGER) RETURNS SETOF foo_type AS $$
    use Digest::MD5 qw(md5_hex);
    my $file = '/usr/share/dict/words';
    my $t = localtime;
    elog(NOTICE, "opening file $file at $t" );
    open my $fh, '<', $file # ¡vaya, es un acceso a un archivo!
        or elog(ERROR, "cannot open $file for reading: $!");
    my @words = <$fh>;
    close $fh;
    $t = localtime;
    elog(NOTICE, "closed file $file at $t");
    chomp(@words);
    my $row;
    my $sth = spi_query("SELECT * FROM generate_series(1,$_[0]) AS b(a)");
    while (defined ($row = spi_fetchrow($sth))) {
        return_next({
            the_num => $row->{a},
            the_text => md5_hex($words[rand @words])
        });
    }
    return;
$$ LANGUAGE plperlu;

SELECT * from lotsa_md5(500);

Normalmente, spi_fetchrow debe repetirse hasta que devuelva undef, lo que indica que no hay más filas que leer. El cursor devuelto por spi_query se libera automáticamente cuando spi_fetchrow devuelve undef. Si no deseas leer todas las filas, llama en su lugar a spi_cursor_close para liberar el cursor. El no hacerlo resultará en fugas de memoria (memory leaks).

spi_prepare(command, argument types)
spi_query_prepared(plan, arguments)
spi_exec_prepared(plan [, attributes], arguments)
spi_freeplan(plan)

spi_prepare, spi_query_prepared, spi_exec_prepared, y spi_freeplan implementan la misma funcionalidad pero para consultas preparadas. spi_prepare acepta una cadena de consulta con marcadores de posición de argumentos numerados ($1, $2, etc.) y una lista de cadenas de tipos de argumentos:

$plan = spi_prepare('SELECT * FROM test WHERE id > $1 AND name = $2',
                                                     'INTEGER', 'TEXT');

Una vez que un plan de consulta se prepara mediante una llamada a spi_prepare, el plan se puede utilizar en lugar de la consulta de cadena, ya sea en spi_exec_prepared, donde el resultado es el mismo que el devuelto por spi_exec_query, o en spi_query_prepared que devuelve un cursor exactamente como lo hace spi_query, que luego se puede pasar a spi_fetchrow. El segundo parámetro opcional para spi_exec_prepared es una referencia a un hash de atributos; el único atributo actualmente admitido es limit, que establece el número máximo de filas devueltas por la consulta. Omitir limit o especificarlo como cero da como resultado que no haya límite de filas.

La ventaja de las consultas preparadas es que es posible utilizar un plan preparado para más de una ejecución de consulta. Después de que el plan ya no sea necesario, se puede liberar con spi_freeplan:

CREATE OR REPLACE FUNCTION init() RETURNS VOID AS $$
        $_SHARED{my_plan} = spi_prepare('SELECT (now() + $1)::date AS now',
                                        'INTERVAL');
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
        return spi_exec_prepared(
                $_SHARED{my_plan},
                $_[0]
        )->{rows}->[0]->{now};
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION done() RETURNS VOID AS $$
        spi_freeplan( $_SHARED{my_plan});
        undef $_SHARED{my_plan};
$$ LANGUAGE plperl;

SELECT init();
SELECT add_time('1 day'), add_time('2 days'), add_time('3 days');
SELECT done();

  add_time  |  add_time  |  add_time
------------+------------+------------
 2005-12-10 | 2005-12-11 | 2005-12-12

Ten en cuenta que el subíndice del parámetro en spi_prepare se define a través de $1, $2, $3, etc., así que evita declarar cadenas de consulta en comillas dobles que puedan fácilmente conducir a errores difíciles de detectar.

Otro ejemplo ilustra el uso de un parámetro opcional en spi_exec_prepared:

CREATE TABLE hosts AS SELECT id, ('192.168.1.'||id)::inet AS address
                      FROM generate_series(1,3) AS id;

CREATE OR REPLACE FUNCTION init_hosts_query() RETURNS VOID AS $$
        $_SHARED{plan} = spi_prepare('SELECT * FROM hosts
                                      WHERE address << $1', 'inet');
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION query_hosts(inet) RETURNS SETOF hosts AS $$
        return spi_exec_prepared(
                $_SHARED{plan},
                {limit => 2},
                $_[0]
        )->{rows};
$$ LANGUAGE plperl;

CREATE OR REPLACE FUNCTION release_hosts_query() RETURNS VOID AS $$
        spi_freeplan($_SHARED{plan});
        undef $_SHARED{plan};
$$ LANGUAGE plperl;

SELECT init_hosts_query();
SELECT query_hosts('192.168.1.0/30');
SELECT release_hosts_query();

    query_hosts
-----------------
 (1,192.168.1.1)
 (2,192.168.1.2)
(2 rows)

spi_commit()
spi_rollback()

Confirma (commit) o revierte (rollback) la transacción actual. Esto solo se puede llamar en un procedimiento o bloque de código anónimo (comando DO) llamado desde el nivel superior. (Ten en cuenta que no es posible ejecutar los comandos SQL COMMIT o ROLLBACK a través de spi_exec_query o similar. Se tiene que hacer utilizando estas funciones). Después de que finaliza una transacción, se inicia automáticamente una nueva transacción, por lo que no hay una función separada para eso.

Aquí hay un ejemplo:

CREATE PROCEDURE transaction_test1()
LANGUAGE plperl
AS $$
foreach my $i (0..9) {
    spi_exec_query("INSERT INTO test1 (a) VALUES ($i)");
    if ($i % 2 == 0) {
        spi_commit();
    } else {
        spi_rollback();
    }
}
$$;

CALL transaction_test1();

43.3.2. Funciones de utilidad en PL/Perl #

elog(level, msg)

Emite un mensaje de registro o error. Los niveles posibles son DEBUG, LOG, INFO, NOTICE, WARNING, y ERROR. ERROR eleva una condición de error; si esta no es capturada por el código Perl circundante, el error se propaga a la consulta de llamada, causando que la transacción o subtransacción actual sea abortada. Esto es efectivamente lo mismo que el comando Perl die. Los otros niveles solo generan mensajes de diferentes niveles de prioridad. Si los mensajes de una prioridad particular se reportan al cliente, se escriben en el registro del servidor o ambos, se controla mediante las variables de configuración log_min_messages y client_min_messages. Consulta la Chapter 19 para obtener más información.

quote_literal(string)

Devuelve la cadena dada adecuadamente entrecomillada para ser utilizada como un literal de cadena en una cadena de sentencia SQL. Las comillas simples y barras invertidas incrustadas se duplican correctamente. Ten en cuenta que quote_literal devuelve undef en una entrada undef; si el argumento puede ser undef, quote_nullable suele ser más adecuado.

quote_nullable(string)

Devuelve la cadena dada adecuadamente entrecomillada para ser utilizada como un literal de cadena en una cadena de sentencia SQL; o, si el argumento es undef, devuelve la cadena sin comillas "NULL". Las comillas simples y barras invertidas incrustadas se duplican correctamente.

quote_ident(string)

Devuelve la cadena dada adecuadamente entrecomillada para ser utilizada como un identificador en una cadena de sentencia SQL. Las comillas se añaden solo si es necesario (es decir, si la cadena contiene caracteres que no son de identificador o se convertirían a minúsculas). Las comillas incrustadas se duplican correctamente.

decode_bytea(string)

Devuelve los datos binarios sin escapar representados por el contenido de la cadena dada, la cual debe estar codificada en bytea.

encode_bytea(string)

Devuelve la forma codificada en bytea del contenido de datos binarios de la cadena dada.

encode_array_literal(array)
encode_array_literal(array, delimiter)

Devuelve el contenido del array referenciado como una cadena en formato de literal de array (consulta la Section 8.15.2). Devuelve el valor del argumento inalterado si no es una referencia a un array. El delimitador utilizado entre los elementos del literal de array por defecto es ", " si no se especifica un delimitador o es undef.

encode_typed_literal(value, typename)

Convierte una variable Perl al valor del tipo de datos pasado como segundo argumento y devuelve una representación de cadena de este valor. Maneja correctamente arrays anizados y valores de tipos compuestos.

encode_array_constructor(array)

Devuelve el contenido del array referenciado como una cadena en formato de constructor de array (consulta la Section 4.2.12). Los valores individuales se entrecomillan utilizando quote_nullable. Devuelve el valor del argumento, entrecomillado con quote_nullable, si no es una referencia a un array.

looks_like_number(string)

Devuelve un valor verdadero si el contenido de la cadena dada parece un número, según Perl, de lo contrario devuelve falso. Devuelve undef si el argumento es undef. El espacio inicial y final se ignora. Inf e Infinity se consideran números.

is_array_ref(argument)

Devuelve un valor verdadero si el argumento dado puede tratarse como una referencia a un array, es decir, si ref del argumento es ARRAY o PostgreSQL::InServer::ARRAY. Devuelve falso de lo contrario.