43.3. Funções integradas #

43.3.1. Acesso a banco de dados no PL/Perl
43.3.2. Funções utilitárias no PL/Perl

43.3.1. Acesso a banco de dados no PL/Perl #

O acesso ao banco de dados a partir de uma função Perl pode ser feito através das seguintes funções:

spi_exec_query(consulta [, limite])

A função spi_exec_query executa um comando SQL e retorna todo o conjunto de linhas como uma referência a uma matriz de referências de hash. Se o limite especificado for maior que zero, então a função spi_exec_query trará no máximo limite linhas, como se a consulta tivesse incluído uma cláusula LIMIT. Omitir o limite, ou especificá-lo como zero, resulta em nenhum limite de linhas.

Só se deve usar este comando quando se souber que o conjunto de resultados será relativamente pequeno. A seguir está um exemplo de uma consulta (comando SELECT) com o número máximo opcional de linhas:

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

Este comando retorna até 5 linhas da tabela minha_tabela. Se minha_tabela tiver a coluna minha_coluna, poderá ser obtido o valor da linha $i do resultado usando:

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

O número total de linhas retornadas de uma consulta pelo comando SELECT pode ser acessado usando:

$nrows = $rv->{processed}

A seguir está um exemplo usando um tipo de comando diferente:

$query = "INSERT INTO minha_tabela VALUES (1, 'teste')";
$rv = spi_exec_query($query);

O status do comando pode acessado (por exemplo, SPI_OK_INSERT) usando:

$res = $rv->{status};

Para obter o número de linhas afetadas, é usado:

$nrows = $rv->{processed};

A seguir está um exemplo completo:

CREATE TABLE teste (
    i int,
    v varchar
);

INSERT INTO teste (i, v) VALUES (1, 'primeira linha');
INSERT INTO teste (i, v) VALUES (2, 'segunda linha');
INSERT INTO teste (i, v) VALUES (3, 'TERCEIRA linha');
INSERT INTO teste (i, v) VALUES (4, 'imortal');

CREATE OR REPLACE FUNCTION test_munge()
    RETURNS SETOF teste AS
$$
    my $rv = spi_exec_query('select i, v from teste;');
    my $status = $rv->{status};
    my $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        my $row = $rv->{rows}[$rn];
        # somar 200 ao valor de 'i'
        $row->{i} += 200 if defined($row->{i});
        # transformar letras maiúsculas em minúsculas, e vice-versa
        $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
        return_next($row);
    }
    return undef;
$$ LANGUAGE plperl;

SELECT * FROM test_munge();

  i  |       v
-----+----------------
 201 | PRIMEIRA LINHA
 202 | SEGUNDA LINHA
 203 | terceira LINHA
 204 | IMORTAL
(4 linhas)

spi_query(comando)
spi_fetchrow(cursor)
spi_cursor_close(cursor)

As funções spi_query e spi_fetchrow trabalham juntas como um par, para conjuntos de linhas que podem ser grandes, ou para casos em que se deseja retornar as linhas conforme elas chegam. A função spi_fetchrow trabalha somente com spi_query. O exemplo a seguir mostra como as duas são usadas juntas:

CREATE TYPE tipo_foo AS (numero INTEGER, texto TEXT);

CREATE OR REPLACE FUNCTION lotsa_md5 (INTEGER) RETURNS SETOF tipo_foo AS $$
    use Digest::MD5 qw(md5_hex);
    my $file = '/usr/share/dict/portuguese';
    my $t = localtime;
    elog(NOTICE, "abrindo arquivo $file em $t" );
    open my $fh, '<', $file # eita, é um acesso a arquivo!
        or elog(ERROR, "não foi possível abrir $file para leitura: $!");
    my @words = <$fh>;
    close $fh;
    $t = localtime;
    elog(NOTICE, "arquivo $file fechado em $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({
            numero => $row->{a},
            texto => md5_hex($words[rand @words])
        });
    }
    return;
$$ LANGUAGE plperlu;

SELECT * from lotsa_md5(5);

NOTA:  abrindo arquivo /usr/share/dict/portuguese em Tue May 19 16:11:58 2026
NOTA:  arquivo /usr/share/dict/portuguese fechado em Tue May 19 16:11:58 2026
 numero |              texto
--------+----------------------------------
      1 | 94eae375e57bed50a91f1b31c665922d
      2 | b67c8dd88d65c6c9fd1ba85f012e32cb
      3 | d81f09cece6834e2e7fdf33dd634631f
      4 | d33e8bd813ff8c67d7c135d30e64988a
      5 | f4fdb4bd7be4b154056617bb2c7502ca
(5 linhas)

Normalmente, a função spi_fetchrow deve ser chamada até que retorne undef, indicando não haver mais linhas para ler. O cursor retornado por spi_query é automaticamente liberado quando spi_fetchrow retorna undef. Se não for desejado ler todas as linhas, deve ser chamada a função spi_cursor_close para liberar o cursor. Não fazer isto resulta em vazamento de memória.

spi_prepare(comando, tipos_de_dados_dos_argumentos)
spi_query_prepared(plano, argumentos)
spi_exec_prepared(plano [, atributos], argumentos)
spi_freeplan(plano)

As funções spi_prepare, spi_query_prepared, spi_exec_prepared e spi_freeplan implementam as mesmas funcionalidades, mas para consultas preparadas. A função spi_prepare aceita uma cadeia de caracteres de consulta com espaços reservados para argumentos numerados ($1, $2, etc.), e uma lista de cadeia de caracteres de tipos de dados dos argumentos:

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

Uma vez que o plano de consulta esteja preparado por uma chamada à função spi_prepare, o plano pode ser usado em vez da cadeia de caracteres da consulta, seja em spi_exec_prepared, onde o resultado é o mesmo que o retornado por spi_exec_query, ou em spi_query_prepared que retorna um cursor exatamente como spi_query faz, que depois pode ser passado para spi_fetchrow. O segundo parâmetro opcional para spi_exec_prepared é uma referência hash de atributos; o único atributo com suporte atualmente é limit, que define o número máximo de linhas retornadas pela consulta. Omitir limite ou especificá-lo como zero resulta em nenhum limite de linhas.

A vantagem das consultas preparadas é ser possível usar um plano preparado para mais de uma execução da consulta. Após não ser mais necessário, o plano poderá ser liberado usando spi_freeplan:

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

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

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

SELECT iniciar();
SELECT adicionar_tempo('1 day'),
       adicionar_tempo('2 days'),
       adicionar_tempo('3 days');
SELECT terminar();

 adicionar_tempo | adicionar_tempo | adicionar_tempo
-----------------+-----------------+-----------------
 2026-05-20      | 2026-05-21      | 2026-05-22
(1 linha)

Note que o índice do parâmetro em spi_prepare é definido via $1, $2, $3, etc., então deve-se evitar declarar cadeias de caracteres de consulta delimitadas que podem facilmente levar a erros difíceis de detectar.

Este outro exemplo mostra o uso de um parâmetro opcional em spi_exec_prepared:

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

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

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

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

SELECT iniciar_consulta_hospedeiros();
SELECT consulta_hospedeiros('192.168.1.0/30');
SELECT liberar_consulta_hospedeiros();

 consulta_hospedeiros
----------------------
 (1,192.168.1.1)
 (2,192.168.1.2)
(2 linhas)

spi_commit()
spi_rollback()

Efetiva (commit), ou desfaz (roll back), a transação corrente. Só podem ser chamados em um procedimento, ou bloco de código anônimo (comando DO), chamado do nível superior. (Note que não é possível executar os comandos COMMIT ou ROLLBACK do SQL via spi_exec_query, ou similar. Tem que ser feito usando estas funções.) Após a transação ser encerrada, uma nova transação é iniciada automaticamente, portanto, não há uma função separada para isto.

A seguir está um exemplo:

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

CALL testa_transacao();

43.3.2. Funções utilitárias no PL/Perl #

elog(nível, mensagem)

Lança um registro ou mensagem de erro. Os níveis possíveis são DEBUG, LOG, INFO, NOTICE, WARNING, e ERROR. ERROR levanta uma condição de erro; se não for interceptado pelo código Perl envoltório, o erro se propagará para a consulta da chamada, fazendo com que a transação ou subtransação corrente seja interrompida. É de fato o mesmo que o comando die do Perl. Os outros níveis geram apenas mensagens de diferentes níveis de prioridade. Se as mensagens de uma determinada prioridade serão mostradas ao cliente, escritas no arquivo de registro de eventos do servidor (log), ou as duas coisas, são controlados pelas variáveis de configuração log_min_messages e client_min_messages. Veja Configuração do servidor para obter mais informações.

quote_literal(cadeia_de_caracteres)

Retorna a cadeia de caracteres fornecida adequadamente delimitada para ser usada como um literal cadeia de caracteres, em uma cadeia de caracteres de instrução do SQL. Apóstrofos e contrabarras incorporados são devidamente duplicados. Note que quote_literal retorna undef para uma entrada undef; se o argumento puder ser undef, a função quote_nullable é geralmente mais adequada.

quote_nullable(cadeia_de_caracteres)

Retorna a cadeia de caracteres fornecida adequadamente delimitada para ser usada como um literal cadeia de caracteres, em uma cadeia de caracteres de instrução do SQL; ou, se o argumento for undef, retorna a cadeia de caracteres "NULL" sem delimitador. Apóstrofos e contrabarras presentes são devidamente duplicados.

quote_ident(cadeia_de_caracteres)

Retorna a cadeia de caracteres fornecida adequadamente delimitada para ser usada como um identificador, em uma cadeia de caracteres de instrução do SQL. Os delimitadores são adicionadas apenas se necessário (ou seja, se a cadeia de caracteres contiver caracteres não identificadores, ou contiver letras maiúsculas e minúsculas). As aspas incorporadas são devidamente duplicadas.

decode_bytea(cadeia_de_caracteres)

Retorna os dados binários sem escape representados pelo conteúdo da cadeia de caracteres fornecida, que deve estar codificada em bytea.

encode_bytea(cadeia_de_caracteres)

Retorna a forma codificada em bytea do conteúdo dos dados binários da cadeia de caracteres fornecida.

encode_array_literal(matriz)
encode_array_literal(matriz, delimitador)

Retorna o conteúdo da matriz referenciada como uma cadeia de caracteres em formato literal de matriz (veja Entrada dos valores da matriz). Retorna o valor do argumento inalterado, se não for uma referência a uma matriz. O delimitador usado entre os elementos do literal matriz assume como padrão a ", ", se o delimitador não for especificado, ou for undef.

encode_typed_literal(valor, nome_do_tipo_de_dados)

Converte uma variável do Perl no valor do tipo de dados passado como segundo argumento, e retorna uma representação de cadeia de caracteres desse valor. Trata corretamente as matrizes aninhadas e valores de tipos de dados compostos.

encode_array_constructor(matriz)

Retorna o conteúdo da matriz referenciada como uma cadeia de caracteres no formato do construtor de matriz (veja Construtores de matriz). Os valores delimitados são delimitados usando quote_nullable. Retorna o valor do argumento, delimitado usando quote_nullable, se não for uma referência a uma matriz.

looks_like_number(cadeia_de_caracteres)

Retorna verdade, se o conteúdo da cadeia de caracteres fornecida parecer um número, segundo o Perl, ou retorna falso caso contrário. Retorna undef se o argumento for undef. Espaços à esquerda e à direita são ignorados. Inf e Infinity são considerados números.

is_array_ref(argumento)

Retorna verdade se o argumento fornecido puder ser tratado como uma referência de matriz, ou seja, se a referência do argumento for ARRAY ou PostgreSQL::InServer::ARRAY. Caso contrário, retorna falso.