Todas as chamadas para funções escritas em uma linguagem diferente da interface “versão 1” corrente para linguagens compiladas (Isso 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. Esse 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
.
Esse 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 possivelmente define 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 esse 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, esse é 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
.
Esse 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 esse tempo.
Uma maneira de fazer isso é alocando os dados extras no contexto de
memória especificado por
flinfo->fn_mcxt
;
esses 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.
Esse é 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. Estas 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, este
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 essa 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 essa
execução.
Em particular, esse parâmetro é desativado pelo
pg_dump para que este 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 esse 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
definitivamente ser 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,
este 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 essas 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 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 56.1. 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.
Nesse exemplo o tratador de linguagem procedural
PL/Sample
é instalado e testado conforme sua
documentação.
$ cd postgresql-14.5/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 (14.5) Type "help" for help. postgres=# CREATE FUNCTION plsample_call_handler() RETURNS language_handler AS '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}'); NOTICE: source text of function "plsample_result_text": Exemplo de fonte com resultado de texto. NOTICE: argument: 0; name: a1; value: 1.23 NOTICE: argument: 1; name: a2; value: abc NOTICE: 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}'); NOTICE: source text of function "plsample_result_void": Exemplo de fonte com resultado nulo. NOTICE: argument: 0; name: a1; value: {foo,bar,hoge} plsample_result_void ---------------------- (1 linha)
Nota: Exemplo escrito pelo tradutor, não fazendo parte da documentação original.