Em vez de executar a consulta inteira de uma vez, é possível
configurar um cursor encapsulando a consulta
e, em seguida, ler o resultado da consulta algumas linhas por vez.
Um motivo para fazer isto é evitar a sobrecarga de memória quando
o resultado contém um grande número de linhas.
(Entretanto, os usuários do PL/pgSQL
normalmente não precisam se preocupar com isto, porque os laços
FOR usam automaticamente um cursor internamente
para evitar problemas de memória.)
Um uso mais interessante é retornar a referência a um cursor que
a função criou, permitindo a quem efetuou a chamada ler as linhas.
Isto fornece uma maneira eficiente de retornar grandes conjuntos
de linhas de funções.
Todo acesso aos cursores no PL/pgSQL
passa por variáveis de cursor, sendo sempre do tipo de dados
especial refcursor.
Uma maneira de criar uma variável de cursor é apenas declará-la
como uma variável do tipo refcursor.
Outra maneira é usar a sintaxe de declaração de cursor, que, em geral é:
nome[ [ NO ] SCROLL ] CURSOR [ (argumentos) ] FORconsulta;
(FOR pode ser substituído por IS
para compatibilidade com o Oracle.)
Se for especificado SCROLL, o cursor poderá
rolar para trás;
se for especificado NO SCROLL, as buscas para
trás serão rejeitadas;
se nenhuma dessas especificações aparecer, fica dependendo da
consulta se as buscas para trás serão permitidas.
Se argumentos for especificado, deverá
ser uma lista separada por vírgulas de pares
, definindo
os nomes a serem substituídos por valores de parâmetro na consulta
fornecida.
Os valores reais que vão substituir estes nomes serão especificados
posteriormente, quando o cursor for aberto.
nome
tipo_de_dados
Alguns exemplos:
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT * FROM tenk1;
curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
Todas estas três variáveis têm o tipo de dados refcursor,
mas a primeira pode ser usada com qualquer consulta, enquanto a
segunda tem uma consulta inteiramente especificada já
vinculada a ela, e a última tem uma consulta
parametrizada vinculada a ela.
(key será substituído por um valor de parâmetro
inteiro quando o cursor for aberto.)
A variável curs1 é dita estar
desvinculada, já que não está vinculada a
nenhuma consulta em particular.
A opção SCROLL não pode ser usada quando a
consulta do cursor usa FOR UPDATE/SHARE.
Além disso, é melhor usar NO SCROLL com uma
consulta envolvendo funções voláteis.
A implementação de SCROLL assume que a releitura
da saída da consulta produzirá resultados consistentes, o que uma
função volátil pode não fazer.
Antes que um cursor possa ser usado para recuperar linhas,
ele deve ser aberto.
(É a ação equivalente ao comando DECLARE
do SQL.)
O PL/pgSQL tem três formas da instrução
OPEN, duas das quais usam variáveis de cursor
não vinculadas, enquanto a terceira usa uma variável de cursor
vinculada.
Variáveis de cursor vinculadas também podem ser usadas sem se abrir
explicitamente o cursor, por meio da instrução FOR
descrita em Laço através do resultado do cursor.
Um laço FOR abrirá o cursor e o fechará
novamente quando o laço for concluído.
Abrir um cursor envolve a criação de uma estrutura de dados interna do servidor chamada portal, que armazena o estado de execução da consulta do cursor. Um portal possui um nome, que deve ser único durante a sessão e enquanto o portal estiver ativo. Por padrão, o PL/pgSQL atribuirá um nome único a cada portal que criar. Entretanto, se for atribuído um valor de cadeia de caracteres não-nulo a uma variável de cursor, esta cadeia de caracteres será usada como nome do portal. Esta funcionalidade pode ser usada conforme descrito em Retorno de cursores.
OPENvariável_de_cursor_não_vinculada[ [ NO ] SCROLL ] FORconsulta;
A variável de cursor é aberta, e recebe a consulta especificada
para executar.
O cursor não pode já estar aberto, devendo ter sido declarado
como uma variável de cursor não vinculada
(ou seja, como uma simples variável refcursor).
A consulta deve ser um comando SELECT,
ou algo que retorne linhas (como o EXPLAIN).
A consulta é tratada da mesma maneira que os demais comandos
SQL no PL/pgSQL:
Os nomes das variáveis do PL/pgSQL
são substituídos, e o plano de consulta é armazenado em cache
para uma possível reutilização.
Quando uma variável do PL/pgSQL
é substituída na consulta do cursor, o valor substituído
é o que ela tinha no momento do OPEN;
alterações subsequentes na variável não afetam o comportamento
do cursor.
As opções SCROLL e NO SCROLL
têm os mesmos significados que para o cursor vinculado.
Exemplo:
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPENvariável_de_cursor_não_vinculada[ [ NO ] SCROLL ] FOR EXECUTEcadeia_de_caracteres_da_consulta[ USINGexpressão[, ... ] ];
A variável de cursor é aberta, e recebe a consulta especificada
para executar.
O cursor não pode já estar aberto, devendo ter sido declarado como
uma variável de cursor não vinculada
(ou seja, como uma simples variável refcursor).
A consulta é especificada como uma expressão de cadeia de caracteres,
da mesma forma que no comando EXECUTE.
Como de costume, isto dá flexibilidade para que o plano de consulta
possa variar de uma execução para outra
(veja Cache de plano),
e também significa não ser feita a substituição de variável na
cadeia de caracteres do comando.
Como acontece com o EXECUTE, os valores dos
parâmetros podem ser inseridos no comando dinâmico através de
format() e USING.
As opções SCROLL e NO SCROLL
têm os mesmos significados que para o cursor vinculado.
Exemplo:
OPEN curs1 FOR EXECUTE
format('SELECT * FROM %I WHERE col1 = $1',tabname)
USING keyvalue;
Neste exemplo, o nome da tabela é inserido na consulta através
da função format().
O valor de comparação para col1 é inserido
por meio de um parâmetro da cláusula USING,
portanto, não precisa de delimitadores.
OPENvariável_de_cursor_vinculado[ ( [nome_do_argumento{ := | => } ]valor_do_argumento[, ...] ) ];
Esta forma do OPEN é usada para abrir uma
variável de cursor cuja consulta foi vinculada a ela quando
foi declarada.
O cursor não pode já estar aberto.
A lista de expressões de valor de argumento deve aparecer se,
e somente se, o cursor tiver sido declarado aceitando argumentos.
Estes valores são substituídos na consulta.
O plano de consulta para um cursor vinculado é sempre
considerado armazenável em cache;
não há equivalente a EXECUTE neste caso.
Note que SCROLL e
NO SCROLL não podem ser especificados no
OPEN, porque o comportamento de rolagem
do cursor já foi determinado.
Os valores dos argumentos podem ser passados usando a notação
posicional ou notacional.
Na notação posicional, todos os argumentos são especificados
em ordem.
Na notação notacional, o nome de cada argumento é especificado
usando := ou => para
separá-lo da expressão do argumento.
De forma semelhante às chamadas de funções, descritas em
Chamadas de função, também é
permitido misturar notação posicional e notacional.
Exemplos (usando os exemplos de declaração de cursor mostrados acima):
OPEN curs2; OPEN curs3(42); OPEN curs3(key := 42); OPEN curs3(key => 42);
Como é feita a substituição de variável na consulta de um cursor
vinculado, existem duas maneiras de passar valores para o cursor:
seja com um argumento explícito para o OPEN,
ou implicitamente referenciando uma variável do
PL/pgSQL na consulta.
Entretanto, apenas as variáveis declaradas antes da declaração
do cursor vinculado serão substituídas nele.
Nos dois casos, o valor a ser passado é determinado no momento do
OPEN.
Por exemplo, outra maneira de obter o mesmo efeito do exemplo
curs3 acima é
DECLARE
key integer;
curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key;
BEGIN
key := 42;
OPEN curs4;
Uma vez aberto o cursor, ele poderá ser manuseado usando as instruções descritas a seguir.
Para começar, estes manuseios não precisam ocorrer na mesma
função que abriu o cursor.
É possível retornar um valor refcursor de uma função,
deixando quem efetuou a chamada operar o cursor.
(Do ponto de vista interno, um valor refcursor é
apenas a cadeia de caracteres do nome de um portal contendo
a consulta ativa para o cursor.
Este nome pode ser passado, atribuído a outras variáveis do
tipo de dados refcursor, e assim por diante,
sem perturbar o portal.)
Todos os portais são implicitamente fechados no final da transação.
Portanto, um valor do tipo de dados refcursor pode
ser usado para referenciar um cursor aberto somente até o final
da transação.
FETCH [direção{ FROM | IN } ]cursorINTOdestino;
FETCH recupera a próxima linha do cursor
(na direção indicada) para o destino, que pode ser uma
variável tipo-linha, uma variável tipo-registro ou uma lista de
variáveis simples separadas por vírgulas, assim como
SELECT INTO.
Se não houver nenhuma linha adequada, o destino será definido
como NULL(s).
Assim como no SELECT INTO, a variável especial
FOUND pode ser verificada para ver se uma linha
foi obtida ou não.
Caso nenhuma linha seja obtida, o cursor será posicionado após
a última linha ou antes da primeira, dependendo da direção do
movimento.
A cláusula direção pode ser qualquer uma
das variantes permitidas no comando SQL
FETCH, exceto aquelas que podem buscar mais
de uma linha; ou seja, pode ser
NEXT,
PRIOR,
FIRST,
LAST,
ABSOLUTE contador,
RELATIVE contador,
FORWARD, ou
BACKWARD.
Omitir a direção é idêntico a
especificar NEXT.
Nas formas usando contador, o
contador pode ser qualquer expressão
que resulte em um valor inteiro
(diferentemente do comando FETCH do
SQL, que permite apenas uma constante inteira).
Os valores de direção que exigem mover
para trás provavelmente falharão, a menos que o cursor tenha sido
declarado ou aberto com a opção SCROLL.
cursor deve ser o nome de uma variável
do tipo refcursor que faz referência a um portal de
cursor aberto.
Exemplos:
FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz; FETCH LAST FROM curs3 INTO x, y; FETCH RELATIVE -2 FROM curs4 INTO x;
MOVE [direção{ FROM | IN } ]cursor;
O comando MOVE reposiciona o cursor sem
recuperar nenhum dado.
O comando MOVE funciona como o comando
FETCH, exceto que ele apenas reposiciona
o cursor e não retorna a linha para a qual foi movido.
A cláusula direção pode ser qualquer
uma das variantes permitidas no comando FETCH
do SQL, incluindo aquelas que podem buscar mais
de uma linha; o cursor é posicionado na última destas linhas.
(Entretanto, no caso em que a cláusula direção
é simplesmente uma expressão de contador
sem palavra-chave está obsoleta no PL/pgSQL.
Esta sintaxe é ambígua no caso em que a cláusula
direção é omitida por completo.,
e, portanto, poderá falhar se o contador
não for uma constante.)
Assim como no SELECT INTO, a variável especial
FOUND pode ser verificada para ver se havia uma
linha para a qual se mover.
Caso não exista tal linha, o cursor será posicionado após a última
linha, ou antes da primeira, dependendo da direção do movimento.
Exemplos:
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
UPDATEtabelaSET ... WHERE CURRENT OFcursor; DELETE FROMtabelaWHERE CURRENT OFcursor;
Quando o cursor é posicionado em uma linha da tabela, esta linha
pode ser atualizada ou excluída usando o cursor para identificar
a linha.
Existem restrições sobre o que pode ser a consulta do cursor
(em particular, nenhum agrupamento), sendo melhor usar
FOR UPDATE no cursor.
Para obter mais informações, consulte a página de referência
da instrução DECLARE.
Exemplo:
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
CLOSE cursor;
CLOSE fecha o portal subjacente a um cursor
aberto.
Pode ser usado para liberar recursos antes do final da transação,
ou para liberar a variável do cursor para ser aberta novamente.
Exemplo:
CLOSE curs1;
As funções PL/pgSQL podem retornar cursores para quem efetuou a chamada, os quais são úteis para retornar várias linhas ou colunas, principalmente em conjuntos de resultados muito grandes. Para fazer isto, a função abre o cursor e retorna o nome do cursor para quem efetuou a chamada (ou apenas abre o cursor usando um nome de portal especificado, ou conhecido por quem efetuou a chamada). Quem efetuou a chamada poderá então buscar as linhas do cursor, que poderá ser fechado por quem efetuou a chamada, ou será fechado automaticamente quando a transação for fechada.
O nome do portal usado para o cursor pode ser especificado
pelo programador, ou gerado automaticamente.
Para especificar um nome de portal, deve-se apenas atribuir
uma cadeia de caracteres à variável refcursor antes
de abri-la.
O valor da cadeia de caracteres da variável refcursor
será usado por OPEN como o nome do portal
subjacente.
Entretanto, se o valor da variável refcursor for nulo
(como é por padrão), então o OPEN irá gerar
automaticamente um nome que não entra em conflito com nenhum
portal existente, e o atribuirá à variável refcursor.
Antes do PostgreSQL 16, as variáveis de cursor vinculado eram inicializadas para conter seus próprios nomes, em vez de serem deixadas como nulas, para que o nome do portal subjacente fosse o mesmo que o nome da variável do cursor por padrão. Isto foi alterado porque criava um risco muito grande de conflitos entre cursores com nomes semelhantes em funções diferentes.
O exemplo a seguir mostra uma maneira pela qual quem chama pode fornecer o nome de um cursor:
CREATE TABLE test (col text);
INSERT INTO test VALUES ('123');
CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS '
BEGIN
OPEN $1 FOR SELECT col FROM test;
RETURN $1;
END;
' LANGUAGE plpgsql;
BEGIN;
SELECT reffunc('funccursor');
FETCH ALL IN funccursor;
COMMIT;
O exemplo a seguir utiliza a geração automática de nomes de cursor:
CREATE FUNCTION reffunc2() RETURNS refcursor AS '
DECLARE
ref refcursor;
BEGIN
OPEN ref FOR SELECT col FROM test;
RETURN ref;
END;
' LANGUAGE plpgsql;
-- é necessário estar em uma transação para usar cursores
BEGIN;
SELECT reffunc2();
reffunc2
--------------------
<unnamed portal 1>
(1 linha)
FETCH ALL IN "<unnamed portal 1>"; COMMIT;
O exemplo a seguir mostra uma maneira de retornar vários cursores a partir de uma única função:
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
BEGIN
OPEN $1 FOR SELECT * FROM table_1;
RETURN NEXT $1;
OPEN $2 FOR SELECT * FROM table_2;
RETURN NEXT $2;
END;
$$ LANGUAGE plpgsql;
-- é necessário estar em uma transação para usar cursores
BEGIN;
SELECT * FROM myfunc('a', 'b');
FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;
Existe uma variante da instrução FOR que permite
iterar pelas linhas retornadas pelo cursor. A sintaxe é:
[ <<rótulo>> ] FORvariável_registroINvariável_de_cursor_vinculado[ ( [nome_do_argumento{ := | => } ]valor_do_argumento[, ...] ) ] LOOPinstruçõesEND LOOP [rótulo];
A variável cursor deve ter sido vinculada a alguma consulta quando
foi declarada, não podendo já estar aberta.
A instrução FOR abre automaticamente o cursor,
e fecha o cursor novamente quando o laço termina.
Deve aparecer uma lista de expressões de valor de argumento se,
e somente se, o cursor foi declarado aceitando argumentos.
Estes valores serão substituídos na consulta, da mesma forma que
durante o OPEN
(veja Abertura de cursor vinculado).
A variável_registro é definida
automaticamente como do tipo de dados record,
existindo apenas dentro do laço (qualquer definição existente do
nome da variável é ignorada dentro do laço).
Cada linha retornada pelo cursor é atribuída sucessivamente a esta
variável de registro, e o corpo do laço é executado.