41.7. Cursores #

41.7.1. Declaração das variáveis de cursor
41.7.2. Abertura de cursores
41.7.3. Uso de cursores
41.7.4. Laço através do resultado do cursor

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.

41.7.1. Declaração das variáveis de cursor #

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 ) ] FOR consulta;

(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 nome tipo_de_dados, 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.

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.

41.7.2. Abertura de cursores #

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.

Nota

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.

41.7.2.1. OPEN FOR consulta #

OPEN variável_de_cursor_não_vinculada
     [ [ NO ] SCROLL ]
     FOR consulta;

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;

41.7.2.2. OPEN FOR EXECUTE #

OPEN variável_de_cursor_não_vinculada
     [ [ NO ] SCROLL ]
     FOR EXECUTE cadeia_de_caracteres_da_consulta
     [ USING expressã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.

41.7.2.3. Abertura de cursor vinculado #

OPEN variá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;

41.7.3. Uso de cursores #

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.

41.7.3.1. FETCH #

FETCH [ direção { FROM | IN } ] cursor INTO destino;

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;

41.7.3.2. MOVE #

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;

41.7.3.3. UPDATE/DELETE WHERE CURRENT OF #

UPDATE tabela SET ... WHERE CURRENT OF cursor;
DELETE FROM tabela WHERE CURRENT OF cursor;

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;

41.7.3.4. CLOSE #

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;

41.7.3.5. Retorno de cursores #

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.

Nota

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;

41.7.4. Laço através do resultado do cursor #

Existe uma variante da instrução FOR que permite iterar pelas linhas retornadas pelo cursor. A sintaxe é:

[ <<rótulo>> ]
FOR variável_registro IN variável_de_cursor_vinculado
    [ ( [ nome_do_argumento { := | => } ] valor_do_argumento [, ...] ) ]
LOOP
    instruções
END 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.