Eu tenho um UDF bastante complicado (move-se em torno de um monte de tabelas, bem como cria um monte de novas) onde podem ocorrer várias interrupções. Antes de cada operação, gostaria de registrar a hora em que uma operação ocorre e a própria consulta. A UDF se parece com o seguinte então:
log function_start
log sql1
execute sql1
log sql2
execute sql2
...
log sqlN
execute sqlN
log function_end
Cada declaração de log significa inserir um novo registro na seguinte tabela:
CREATE TABLE backup_logs
(
id serial NOT NULL,
t timestamp with time zone default now(),
sql text,
CONSTRAINT backup_logs_pkey PRIMARY KEY (id)
)
Em caso de aborto, gostaria sql1, sql2, ... sqlN
de ser revertido, mas inserto into backup_logs
para persistir. Pergunta: como posso conseguir isso?
Quando ocorre uma exceção, tudo é revertido. Você deseja executar o código selecionado "fora" do contexto da transação atual, que é comumente referido como transações autônomas . Isso não está implementado atualmente (a partir da página 9.4). Há um item no wiki do Postgres TODO , mas é uma questão complicada, não prenda a respiração.
Por enquanto você pode usar o módulo adicional
dblink
. Ele fornece funções para se conectar a outro banco de dados em uma conexão separada (e, portanto, também uma transação separada) para executar o SQL lá. O que está feito está feito e não pode ser revertido. Ao conectar-se ao mesmo banco de dados outra vez, você efetivamente obtém transações autônomas (com alguma sobrecarga extra). É decentemente rápido.Prova de conceito
Preparações
Você precisa instalar o dblink uma vez por banco de dados (onde você executa a função):
Por conveniência, encapsulo as informações de conexão em um sinal de
FOREIGN SERVER
adiçãoUSER MAPPING
:Existem várias opções para a string de conexão. O exemplo é para um usuário nomeado
role_source
se conectar comorole_target
em um banco de dadostest
em localhost127.0.0.1
. Pode ser o mesmo nome de função para origem e destino. Eu uso uma senha aqui (exemplo simples), mas prefiro muito mais o acesso sem senha para esse fim, para que não precisemos salvar um PW (e não acabe em backups etc.):No entanto, por documentação :
Ou crie uma
SECURITY DEFINER
função para encapsular partes relevantes. Veja abaixo.Função principal
Certifique-se de ter um
USER MAPPING
para a função com a qual está executando esta função ou torne-a umaSECURITY DEFINER
função pertencente à função com a qual deseja trabalhar: