Estou tentando aprender procedimentos armazenados do PostgreSQL. Especificamente criando o procedimento abaixo em PSQL.
CREATE OR REPLACE PROCEDURE BUILD_AND_POPULATE(INOUT cresults refcursor)
LANGUAGE PLPGSQL
AS $$
BEGIN
BEGIN; -- I've tried removing this but the behaviour is the same
cresults:= 'cur';
DROP TABLE IF EXISTS procsampledata;
CREATE TABLE procsampledata as select x,1 as c2,2 as c3, md5(random()::text) from generate_series(1,10) x;
COMMIT;
OPEN cresults FOR SELECT * FROM procsampledata;
END;
$$;
Então eu executo assim, mas recebo um erro:
postgres=# call build_and_populate(null);
ERROR: invalid transaction termination
CONTEXT: PL/pgSQL function build_and_populate(refcursor) line 6 at COMMIT
Eu tentei com AUTOCOMMIT definido para ligado e desligado.
Esta é a minha versão do Postgres
PostgreSQL 11.5 on x86_64-pc-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit
Alguém pode ver o que estou fazendo de errado? Obrigada!
o
está errado e causará um erro. Você não pode iniciar uma transação dentro de um procedimento, pois já existe uma transação ativa.
Você pode encerrar uma transação, o que implica que uma nova transação seja iniciada imediatamente.
Sem o
BEGIN;
, seu procedimento funciona bem.O problema deve estar na forma como você está chamando. Como diz a documentação :
Provavelmente há um
SELECT
na sua pilha de chamadas.Outra possibilidade é que você tenha iniciado explicitamente uma transação
BEGIN
antes de chamar o procedimento armazenado.Isso também não funciona, que é uma restrição de implementação não documentada que pode ser corrigida no futuro. Veja este tópico para referência.
Depois de estabelecer que
BEGIN;
não é permitido em um procedimento e que o lado do clienteCALL
não deve ocorrer a partir de uma transação já iniciada, a questão se resume a (como você escreveu como comentário):Ainda é possível. Existem dois tipos de CURSORs:
WITH HOLD
transações de sobrevivência eWITHOUT HOLD
(o padrão) não. Você pode apontar uma variável refcursor para umWITH HOLD
cursor em plpgsql com um pouco de SQL dinâmico. Aqui está uma versão modificada do seu procedimento fazendo isso: