En lugar de ejecutar una consulta completa a la vez, es posible configurar
un cursor que encapsule la consulta y luego leer el
resultado de la consulta unas pocas filas a la vez. Una razón para hacer esto es
evitar el desbordamiento de memoria cuando el resultado contiene una gran cantidad de
filas. (Sin embargo, los usuarios de PL/pgSQL no suelen necesitar
preocuparse por eso, ya que los bucles FOR utilizan automáticamente un cursor
de forma interna para evitar problemas de memoria). Un uso más interesante es devolver
una referencia a un cursor que una función ha creado, permitiendo al llamador leer las filas.
Esto proporciona una forma eficiente de devolver grandes conjuntos de filas desde funciones.
Todo el acceso a los cursores en PL/pgSQL se realiza a través de
variables de cursor, que son siempre del tipo de datos especial refcursor.
Una forma de crear una variable de cursor es simplemente declararla como una variable de
tipo refcursor. Otra forma es utilizar la sintaxis de declaración de cursor,
que en general es:
name[ [ NO ] SCROLL ] CURSOR [ (arguments) ] FORquery;
(FOR se puede reemplazar por IS para
compatibilidad con Oracle).
Si se especifica SCROLL, el cursor podrá desplazarse hacia atrás;
si se especifica NO SCROLL, se rechazarán las lecturas hacia atrás;
si no aparece ninguna especificación, dependerá de la consulta si se permiten las lecturas
hacia atrás. Los arguments, si se especifican, son una lista
separada por comas de pares
que definen los nombres que se reemplazarán por los valores de los parámetros en la consulta dada.
Los valores reales a sustituir por estos nombres se especificarán más adelante, cuando se abra
el cursor.
name datatype
Algunos ejemplos:
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT * FROM tenk1;
curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
Las tres variables tienen el tipo de datos refcursor, pero la primera se puede
usar con cualquier consulta, mientras que la segunda ya tiene una consulta completamente
especificada vinculada (bound) a ella, y la última tiene una consulta parametrizada
vinculada a ella. (key se reemplazará por un valor de parámetro entero cuando
se abra el cursor). Se dice que la variable curs1 está
desvinculada (unbound) ya que no está vinculada a ninguna consulta en particular.
La opción SCROLL no se puede utilizar cuando la consulta del cursor utiliza
FOR UPDATE/SHARE. Además, es mejor usar NO SCROLL con una
consulta que involucre funciones volátiles. La implementación de SCROLL asume
que volver a leer la salida de la consulta dará resultados consistentes, lo cual podría no ocurrir
con una función volátil.
Antes de poder utilizar un cursor para recuperar filas, este debe ser abierto.
(Esta es la acción equivalente al comando SQL DECLARE CURSOR).
PL/pgSQL tiene tres formas de la instrucción OPEN,
dos de las cuales utilizan variables de cursor desvinculadas, mientras que la tercera utiliza una variable
de cursor vinculada.
Las variables de cursor vinculadas también se pueden utilizar sin abrir explícitamente el cursor,
a través de la instrucción FOR descrita en
Section 41.7.4.
Un bucle FOR abrirá el cursor y luego lo cerrará nuevamente cuando se complete
el bucle.
Abrir un cursor implica crear una estructura de datos interna del servidor llamada portal, que contiene el estado de ejecución de la consulta del cursor. Un portal tiene un nombre, que debe ser único dentro de la sesión durante la existencia del portal. Por defecto, PL/pgSQL asignará un nombre único a cada portal que cree. Sin embargo, si asignas un valor de cadena no nulo a una variable de cursor, esa cadena se utilizará como su nombre de portal. Esta característica se puede utilizar como se describe en Section 41.7.3.5.
OPEN FOR query #OPENunbound_cursorvar[ [ NO ] SCROLL ] FORquery;
La variable de cursor se abre y se le proporciona la consulta especificada para ejecutar.
El cursor no puede estar ya abierto y debe haber sido declarado como una variable de cursor
desvinculada (es decir, como una simple variable refcursor). La consulta debe ser
un SELECT o alguna otra cosa que devuelva filas (como EXPLAIN).
La consulta se trata de la misma manera que otros comandos SQL en PL/pgSQL:
se sustituyen los nombres de las variables de PL/pgSQL y el plan de consulta
se almacena en caché para su posible reutilización. Cuando una variable de PL/pgSQL
se sustituye en la consulta del cursor, el valor que se sustituye es el que tiene en el momento del
OPEN; los cambios posteriores en la variable no afectarán al comportamiento del cursor.
Las opciones SCROLL y NO SCROLL tienen los mismos significados
que para un cursor vinculado.
Un ejemplo:
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPEN FOR EXECUTE #OPENunbound_cursorvar[ [ NO ] SCROLL ] FOR EXECUTEquery_string[ USINGexpression[, ... ] ];
La variable de cursor se abre y se le proporciona la consulta especificada para ejecutar.
El cursor no puede estar ya abierto y debe haber sido declarado como una variable de cursor
desvinculada (es decir, como una simple variable refcursor). La consulta se
especifica como una expresión de cadena, de la misma manera que en el comando
EXECUTE. Como es habitual, esto proporciona flexibilidad para que el plan
de consulta pueda variar de una ejecución a otra (consulta la Section 41.11.2),
y también significa que no se realiza la sustitución de variables en la cadena de comando.
Al igual que con EXECUTE, los valores de los parámetros se pueden insertar
en el comando dinámico a través de format() y USING.
Las opciones SCROLL y NO SCROLL tienen los mismos
significados que para un cursor vinculado.
Un ejemplo:
OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING keyvalue;
En este ejemplo, el nombre de la tabla se inserta en la consulta a través de
format(). El valor de comparación para col1 se
inserta mediante un parámetro USING, por lo que no necesita comillas.
OPENbound_cursorvar[ ( [argument_name{ := | => } ]argument_value[, ...] ) ];
Esta forma de OPEN se utiliza para abrir una variable de cursor
cuya consulta se vinculó a ella cuando se declaró. El cursor no puede estar ya abierto.
Debe aparecer una lista de expresiones de valores de argumentos reales si y solo si
el cursor fue declarado para recibir argumentos. Estos valores se sustituirán en la consulta.
El plan de consulta para un cursor vinculado siempre se considera almacenable en caché;
no hay equivalente de EXECUTE en este caso. Ten en cuenta que
SCROLL y NO SCROLL no se pueden especificar en
OPEN, ya que el comportamiento de desplazamiento del cursor ya estaba determinado.
Los valores de los argumentos se pueden pasar utilizando notación posicional
o nominal. En la notación posicional, todos los argumentos se especifican
en orden. En la notación nominal, el nombre de cada argumento se especifica utilizando
:= o => para separarlo de la expresión del argumento.
De manera similar a la llamada a funciones, descrita en Section 4.3,
también se permite mezclar la notación posicional y nominal.
Ejemplos (estos utilizan los ejemplos de declaración de cursor anteriores):
OPEN curs2; OPEN curs3(42); OPEN curs3(key := 42); OPEN curs3(key => 42);
Dado que la sustitución de variables se realiza en la consulta de un cursor vinculado, realmente
hay dos formas de pasar valores al cursor: con un argumento explícito en OPEN,
o implícitamente haciendo referencia a una variable de PL/pgSQL en la consulta.
Sin embargo, solo las variables declaradas antes de declarar el cursor vinculado se sustituirán en él.
En cualquier caso, el valor a pasar se determina en el momento del OPEN.
Por ejemplo, otra forma de obtener el mismo efecto que el ejemplo de curs3 anterior es:
DECLARE
key integer;
curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key;
BEGIN
key := 42;
OPEN curs4;
Una vez abierto un cursor, este se puede manipular con las instrucciones que se describen aquí.
Estas manipulaciones no necesitan ocurrir en la misma función que abrió el cursor inicialmente.
Puedes devolver un valor refcursor desde una función y permitir que el llamador opere
en el cursor. (Internamente, un valor refcursor es simplemente el nombre en cadena del
portal que contiene la consulta activa para el cursor. Este nombre se puede pasar, asignar a otras
variables refcursor, etc., sin alterar el portal).
Todos los portales se cierran implícitamente al final de la transacción. Por lo tanto, un valor
refcursor solo es utilizable para hacer referencia a un cursor abierto hasta el final
de la transacción.
FETCH #FETCH [direction{ FROM | IN } ]cursorINTOtarget;
FETCH recupera la siguiente fila (en la dirección indicada) desde el
cursor hacia un objetivo, que puede ser una variable de fila, una variable de registro
o una lista de variables simples separadas por comas, al igual que SELECT INTO.
Si no hay una fila adecuada, el objetivo se establece en NULL(s). Al igual que con SELECT INTO,
la variable especial FOUND se puede comprobar para ver si se obtuvo una fila o no.
Si no se obtiene ninguna fila, el cursor se posiciona después de la última fila o antes de la primera fila,
según la dirección del movimiento.
La cláusula direction puede ser cualquiera de las variantes permitidas en el
comando SQL FETCH, excepto las que pueden recuperar más de una fila; a saber,
puede ser NEXT, PRIOR, FIRST, LAST,
ABSOLUTE count, RELATIVE count,
FORWARD o BACKWARD. Omitir la direction es lo mismo
que especificar NEXT. En las formas que utilizan un count,
este puede ser cualquier expresión con valor entero (a diferencia del comando SQL FETCH,
que solo permite una constante entera). Los valores de direction que requieren moverse
hacia atrás probablemente fallarán a menos que el cursor se haya declarado o abierto con la opción
SCROLL.
El cursor debe ser el nombre de una variable refcursor
que haga referencia a un portal de cursor abierto.
Ejemplos:
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 #MOVE [direction{ FROM | IN } ]cursor;
MOVE vuelve a posicionar un cursor sin recuperar ningún dato.
MOVE funciona como el comando FETCH, excepto que solo vuelve
a posicionar el cursor y no devuelve la fila a la que se movió. La cláusula direction
puede ser cualquiera de las variantes permitidas en el comando SQL FETCH,
incluyendo aquellas que pueden recuperar más de una fila; el cursor se posiciona en la última de esas filas.
(Sin embargo, el caso en el que la cláusula direction es simplemente una expresión
count sin palabra clave está en desuso en PL/pgSQL.
Esa sintaxis es ambigua con el caso donde la cláusula direction se omite por
completo y, por lo tanto, puede fallar si el count no es una constante).
Al igual que con SELECT INTO, la variable especial FOUND se puede
comprobar para ver si había una fila a la que moverse. Si no existe tal fila, el cursor se posiciona después
de la última fila o antes de la primera fila, según la dirección del movimiento.
Ejemplos:
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
UPDATE/DELETE WHERE CURRENT OF #UPDATEtableSET ... WHERE CURRENT OFcursor; DELETE FROMtableWHERE CURRENT OFcursor;
Cuando un cursor se posiciona en una fila de tabla, esa fila se puede actualizar o eliminar
utilizando el cursor para identificarla. Hay restricciones sobre lo que puede ser la consulta del
cursor (en particular, no agrupamiento) y es mejor utilizar FOR UPDATE en el
cursor. Para obtener más información, consulta la página de referencia de DECLARE.
Un ejemplo:
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
CLOSE #
CLOSE cursor;
CLOSE cierra el portal subyacente a un cursor abierto.
Esto se puede utilizar para liberar recursos antes del final de la transacción,
o para liberar la variable de cursor para que se pueda abrir de nuevo.
Un ejemplo:
CLOSE curs1;
Las funciones de PL/pgSQL pueden devolver cursores al llamador. Esto es útil para devolver múltiples filas o columnas, especialmente con conjuntos de resultados muy grandes. Para hacer esto, la función abre el cursor y devuelve el nombre del cursor al llamador (o simplemente abre el cursor utilizando un nombre de portal especificado por o conocido de otra manera por el llamador). El llamador puede entonces recuperar (fetch) filas del cursor. El cursor puede ser cerrado por el llamador, o se cerrará automáticamente cuando se cierre la transacción.
El nombre del portal utilizado para un cursor lo puede especificar el programador o generarse
automáticamente. Para especificar un nombre de portal, simplemente asigna una cadena a la variable
refcursor antes de abrirla. El valor de cadena de la variable refcursor
será utilizado por OPEN como el nombre del portal subyacente. Sin embargo, si el
valor de la variable refcursor es nulo (como lo será por defecto), entonces
OPEN genera automáticamente un nombre que no entra en conflicto con ningún portal
existente y lo asigna a la variable refcursor.
Antes de PostgreSQL 16, las variables de cursor vinculadas se inicializaban para contener sus propios nombres, en lugar de dejarse como nulas, de modo que el nombre del portal subyacente sería el mismo que el nombre de la variable de cursor por defecto. Esto se cambió porque creaba demasiado riesgo de conflictos entre cursores con nombres similares en diferentes funciones.
El siguiente ejemplo muestra una forma en que el llamador puede suministrar un nombre de 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;
El siguiente ejemplo utiliza la generación automática de nombres de cursor:
CREATE FUNCTION reffunc2() RETURNS refcursor AS '
DECLARE
ref refcursor;
BEGIN
OPEN ref FOR SELECT col FROM test;
RETURN ref;
END;
' LANGUAGE plpgsql;
-- se necesita estar en una transacción para usar cursores.
BEGIN;
SELECT reffunc2();
reffunc2
--------------------
<unnamed cursor 1>
(1 row)
FETCH ALL IN "<unnamed cursor 1>";
COMMIT;
El siguiente ejemplo muestra una forma de devolver múltiples cursores desde una sola función:
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;
-- se necesita estar en una transacción para usar cursores.
BEGIN;
SELECT * FROM myfunc('a', 'b');
FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;
Existe una variante de la instrucción FOR que permite iterar a través de las filas
devueltas por un cursor. La sintaxis es:
[ <<label>> ] FORrecordvarINbound_cursorvar[ ( [argument_name{ := | => } ]argument_value[, ...] ) ] LOOPstatementsEND LOOP [label];
La variable de cursor debe haberse vinculado a alguna consulta cuando se declaró, y no
puede estar ya abierta. La instrucción FOR abre automáticamente el cursor, y lo vuelve
a cerrar cuando se sale del bucle. Debe aparecer una lista de expresiones de valores de argumentos reales
si y solo si el cursor fue declarado para recibir argumentos. Estos valores se sustituirán en la consulta,
de la misma manera que durante un OPEN (consulta la Section 41.7.2.3).
La variable recordvar se define automáticamente como tipo record
y existe solo dentro del bucle (cualquier definición existente del nombre de la variable se ignora dentro
del bucle). Cada fila devuelta por el cursor se asigna sucesivamente a esta variable de registro y se ejecuta
el cuerpo del bucle.