Todas as chamadas para funções escritas em uma linguagem diferente da interface “versão 1” corrente para linguagens compiladas (isto inclui funções em linguagens procedurais definidas pelo usuário e funções escritas em SQL) passam por uma função tratadora de chamadas específica para a linguagem. É responsabilidade do tratador de chamadas executar a função de maneira apropriada, por exemplo, interpretando o código-fonte fornecido. Este capítulo delineia como o tratador de chamadas de uma nova linguagem procedural pode ser escrito.
O tratador de chamadas para uma linguagem procedural é uma função
“normal”, que deve ser escrita em uma linguagem
compilada, como C, usando a interface versão-1,
e registrada no PostgreSQL como sem
argumentos e retornando o tipo de dados language_handler.
Este pseudotipo especial identifica a função como tratadora de
chamadas, impedindo que seja chamada diretamente em comandos
SQL.
Para obter mais detalhes sobre as convenções de chamada da linguagem
C e carregamento dinâmico, veja
Funções na linguagem C.
O tratador de chamadas é chamado da mesma forma que qualquer outra
função: recebe um ponteiro para a estrutura
FunctionCallInfoBaseData contendo os
valores dos argumento e outras informações sobre a função chamada.
Retorna um resultado do tipo de dados Datum
(e define possivelmente o campo isnull
da estrutura FunctionCallInfoBaseData, se
desejar retornar um resultado SQL nulo).
A diferença entre um tratador de chamadas e uma chamada de função
comum, é que o campo flinfo->fn_oid
da estrutura FunctionCallInfoBaseData
contém o OID da função real a ser chamada, e não o do próprio
tratador de chamadas.
O tratador de chamadas deve usar este campo para determinar qual
função executar.
Além disso, a lista de argumentos passados é configurada segundo
a declaração da função de destino, e não a do tratador de chamadas.
Cabe ao tratador de chamadas buscar a entrada da função no catálogo
do sistema pg_proc, e analisar os tipos de
dados dos argumentos e de retorno da função chamada.
A cláusula AS do comando
CREATE FUNCTION para a função será encontrada na
coluna prosrc da linha pg_proc.
Geralmente, este é o texto-fonte na linguagem procedural, mas, em
teoria, pode ser outra coisa, como um nome de caminho para um
arquivo, ou qualquer outra coisa que diga ao tratador de chamadas
o que fazer em detalhes.
Muitas vezes, a mesma função é chamada várias vezes na mesma
instrução SQL.
O tratador de chamadas pode evitar procuras de informações repetidas
sobre a função chamada usando o campo
flinfo->fn_extra.
Este campo, que é inicialmente NULL, pode ser
definido pelo tratador de chamadas para apontar para informações
sobre a função chamada.
Nas chamadas seguintes, se
flinfo->fn_extra não for
NULL, então poderá ser usado e a etapa de
procura de informações ignorada.
O tratador de chamadas deve certificar-se de que
flinfo->fn_extra aponta para uma
área de memória que permanecerá alocada pelo menos até
o final da consulta corrente, porque a estrutura de dados
FmgrInfo pode permanecer durante este tempo.
Uma maneira de fazer isto é alocando os dados extras no contexto de
memória especificado por
flinfo->fn_mcxt;
estes dados normalmente terão o mesmo tempo de vida que a própria
estrutura FmgrInfo.
Mas o tratador também pode optar por usar um contexto de memória
de vida mais longa, para poder armazenar em cache as informações
de definição da função ao longo de várias consultas.
Quando uma função de linguagem procedural é chamada como um gatilho,
nenhum argumento é passado da maneira usual, mas o campo
context da estrutura
FunctionCallInfoBaseData aponta para uma
estrutura TriggerData, em vez de ser
NULL como em uma chamada de função comum.
O tratador da linguagem deve fornecer mecanismos para funções de
linguagem procedural para obter as informações do gatilho.
Um modelo para tratador de linguagem procedural escrito como uma
extensão C é fornecido no diretório
src/test/modules/plsample
da distribuição do código-fonte.
Este é um exemplo funcional, que mostra uma maneira de criar
um tratador de linguagem procedural, processar parâmetros, e
retornar um valor.
Embora fornecer um tratador de chamadas seja suficiente para criar uma linguagem procedural mínima, há duas outras funções que podem ser opcionalmente fornecidas para tornar o uso da linguagem mais conveniente. Elas são um validador e um tratador em-linha. O validador pode ser fornecido para permitir que seja feita a verificação específica da linguagem durante CREATE FUNCTION. O tratador em-linha pode ser fornecido para permitir que a linguagem dê suporte a blocos de código anônimos executados por meio do comando DO.
Se a linguagem procedural fornecer um validador, ele
deverá ser declarado como uma função que recebe um único parâmetro
do tipo de dados oid.
O resultado do validador é ignorado, sendo normalmente
declarado retornando void.
O validador será chamado ao final de um comando
CREATE FUNCTION que tenha criado ou atualizado
uma função escrita na linguagem procedural.
O OID passado é o OID da linha pg_proc da função.
O validador deve buscar esta linha da maneira usual, e fazer qualquer
verificação apropriada.
Primeiro, chamar CheckFunctionValidatorAccess()
para diagnosticar chamadas explícitas ao validador que o usuário não
conseguiu realizar por meio de CREATE FUNCTION.
As verificações típicas incluem verificar se os tipos de dados dos
argumentos e do resultado da função têm suporte pela linguagem,
e se o corpo da função está sintaticamente correto na linguagem.
Se o validador achar que a função está correta, deverá apenas
retornar.
Se encontrar um erro, deverá relatá-lo através do mecanismo normal
de relatório de erro ereport().
Lançar um erro forçará uma reversão da transação e, assim, evitar
que uma definição de função incorreta seja efetivada.
As funções do validador normalmente devem respeitar o parâmetro
check_function_bodies: se estiver desativado,
qualquer verificação cara ou sensível ao contexto deverá ser ignorada.
Se a linguagem permitir a execução do código no momento da compilação,
o validador deverá suprimir as verificações que induziriam a esta
execução.
Em particular, este parâmetro é desativado pelo
pg_dump para que ele possa carregar as
funções da linguagem procedural sem se preocupar com efeitos
colaterais, ou dependências dos corpos das funções em outros
objetos do banco de dados.
(Devido a este requisito, o tratador de chamadas deve evitar
assumir que o validador verificou totalmente a função.
O objetivo de ter um validador não é permitir que o tratador de
chamadas omita as verificações, mas notificar o usuário
imediatamente se houver erros óbvios no comando
CREATE FUNCTION.)
Embora a escolha de exatamente o que verificar seja deixada a
critério da função do validador, note que o núcleo do comando
CREATE FUNCTION apenas executa as cláusulas
SET anexadas a uma função quando
check_function_bodies está ativo.
Portanto, as verificações cujos resultados podem ser afetados pelos
parâmetros GUC devem
ser definitivamente ignoradas quando
check_function_bodies está desativado,
para evitar falsas falhas ao recuperar uma cópia de segurança.
Se a linguagem procedural fornecer um tratador em-linha,
ele deverá ser declarado como uma função que recebe um único
parâmetro do tipo internal.
O resultado do tratador em-linha é ignorado, então é normalmente
declarado retornando void.
O tratador em-linha será chamado quando uma instrução
DO for executada especificando esta linguagem
procedural.
O parâmetro realmente passado é um ponteiro para uma estrutura
InlineCodeBlock, que contém informações
sobre os parâmetros da instrução DO,
em particular o texto do bloco de código anônimo a ser executado.
O tratador em-linha deve executar este código e retornar.
É recomendável envolver todas estas declarações de função, bem como
o próprio comando CREATE LANGUAGE, em uma
extensão, para que um simples comando
CREATE EXTENSION seja suficiente para instalar
a linguagem.
Veja Empacotamento de objetos relacionados em uma extensão para obter informações
sobre como escrever extensões.
As linguagens procedurais incluídas na distribuição padrão são boas
referências ao tentar escrever seu próprio tratador de linguagem.
Procure no subdiretório src/pl da árvore de
distribuição do código-fonte.
A página de referência CREATE LANGUAGE
também possui alguns detalhes úteis.
Exemplo 57.1. Exemplo do tradutor
Teste do tratador da linguagem procedural PL/Sample
PL/Sample é um exemplo/modelo de tratador de linguagem procedural. É uma implementação simples, mas demonstra algumas das coisas que podem ser feitas para construir um tratador de linguagem procedural totalmente funcional.
Neste exemplo o tratador de linguagem procedural
PL/Sample é instalado e testado conforme sua
documentação.
$ cd postgresql-18.1/src/test/modules/plsample/
$ make
$ sudo make install
...
/usr/bin/install -c -m 755 plsample.so '/usr/local/pgsql/lib/'
$ sudo su - postgres
$ psql
psql (18.3 (Debian 18.3-1.pgdg12+1))
Digite "help" para obter ajuda.
postgres=# CREATE FUNCTION plsample_call_handler() RETURNS language_handler
AS '/usr/local/pgsql/lib/plsample'
LANGUAGE C;
CREATE FUNCTION
postgres=# CREATE LANGUAGE plsample
HANDLER plsample_call_handler;
CREATE LANGUAGE
postgres=# CREATE FUNCTION plsample_result_text(a1 numeric, a2 text, a3 integer[])
RETURNS TEXT
AS $$
Exemplo de fonte com resultado de texto.
$$ LANGUAGE plsample;
CREATE FUNCTION
postgres=# SELECT plsample_result_text(1.23, 'abc', '{4, 5, 6}');
NOTA: source text of function "plsample_result_text":
Exemplo de fonte com resultado de texto.
NOTA: argument: 0; name: a1; value: 1.23
NOTA: argument: 1; name: a2; value: abc
NOTA: argument: 2; name: a3; value: {4,5,6}
plsample_result_text
--------------------------------------------
+
Exemplo de fonte com resultado de texto.+
(1 linha)
postgres=# CREATE FUNCTION plsample_result_void(a1 text[])
RETURNS VOID
AS $$
Exemplo de fonte com resultado nulo.
$$ LANGUAGE plsample;
CREATE FUNCTION
postgres=# SELECT plsample_result_void('{foo, bar, hoge}');
NOTA: source text of function "plsample_result_void":
Exemplo de fonte com resultado nulo.
NOTA: argument: 0; name: a1; value: {foo,bar,hoge}
plsample_result_void
----------------------
(1 linha)