Capítulo 56. Escrita de tratador de linguagem procedural

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.


Contato

CSS válido!