Error: función set_valued llamada en contexto que no puede aceptar un conjunto. ¿De qué se trata?

11

Yo uso Postgresql 9.1, con ubuntu 12.04.

Inspirado por la respuesta de Craig a mi pregunta concatenación de tipo setof o registro setof pensé que iba a ir bien con el uso return query, setof recordy un generador de serie en esta función plpgsql:

create or replace function compute_all_pair_by_craig(id_obj bigint)
    returns setof record as $$
begin
    return query select o.id, generate_series(0,o.value) from m_obj as o;     
end;
$$    language plpgsql;

Durante la ejecución me sale el error:

ERROR: set_valued function called in context that cannot accept a set

Qué está mal ? Al contrario de Craig, le digo a la función que regrese setof record.

Puedo lograr algo que funcione haciendo exactamente como Craig, es decir, definiendo un tipo create type pair_id_value as (idx bigint, value integer)y haciendo que mi función plpgsql devuelva a en setof of pair_id_valuelugar de a setof record.

Pero incluso con esta solución de trabajo, todavía no entiendo por qué select id, generate_series(0,13)solo devolverá un resultado en dos columnas ... y, por el contrario, llamar a la función (devuelve setof pair_id_value) con return query select id, generate_series(0,my_obj.value) from my_objdevolverá un resultado en solo una columna cuyo campo se parece esto "(123123,0)" "(123123,1)" "(123123,2)" (3 filas) que obviamente son tuplas.

¿Es un caso donde se debe / se debe crear una tabla temporal?

Stephane Rolland
fuente
Ese no puede ser el texto exacto de la función que está ejecutando porque no se compila; hay un punto BEGINy coma en exceso después y uno que falta después del RETURN QUERY. Después de corregir esos errores, confirmo el error al regresar record; explicará en respuesta.
Craig Ringer el
@CraigRinger Puse el punto y coma de nuevo en su lugar.
Stephane Rolland

Respuestas:

7

El mensaje de error no es muy útil:

regress=> SELECT * FROM  compute_all_pair_by_craig(100);
ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM  compute_all_pair_by_craig(100);

pero si reformula la consulta para llamarla como una función de devolución de conjuntos adecuada, verá el verdadero problema:

regress=> SELECT * FROM compute_all_pair_by_craig(100);
ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM compute_all_pair_by_craig(100);

Si utiliza SETOF RECORDsin una OUTlista de parámetros, debe especificar los resultados en la declaración de llamada, por ejemplo:

regress=> SELECT * FROM compute_all_pair_by_craig(100) theresult(a integer, b integer);

Sin embargo, es mucho mejor usar RETURNS TABLEo OUTparámetros. Con la sintaxis anterior, su función sería:

create or replace function compute_all_pair_by_craig(id_obj bigint)
    returns table(a integer, b integer) as $$
begin
    return query select o.id, generate_series(0,o.value) from m_obj as o;     
end;
$$ language plpgsql;

Esto se puede llamar en el contexto de la lista SELECT y se puede usar sin crear un tipo explícitamente o sin especificar la estructura de resultados en el sitio de la llamada.


En cuanto a la segunda mitad de la pregunta, lo que sucede es que el primer caso especifica dos columnas separadas en una lista SELECT, mientras que el segundo devuelve un único compuesto. En realidad, no tiene que ver con cómo está devolviendo el resultado, sino cómo está invocando la función. Si creamos la función de muestra:

CREATE OR REPLACE FUNCTION twocols() RETURNS TABLE(a integer, b integer) 
AS $$ SELECT x, x FROM generate_series(1,5) x; $$ LANGUAGE sql;

Verá la diferencia en las dos formas de llamar a una función de retorno de conjunto: en la SELECTlista, una extensión no estándar específica de PostgreSQL con un comportamiento peculiar:

regress=> SELECT twocols();
 twocols 
---------
 (1,1)
 (2,2)
 (3,3)
 (4,4)
 (5,5)
(5 rows)

o como una tabla de la manera más estándar:

regress=> SELECT * FROM twocols();
 a | b 
---+---
 1 | 1
 2 | 2
 3 | 3
 4 | 4
 5 | 5
(5 rows)
Craig Ringer
fuente
Recién probado, funciona perfecto. Y me gusta esta sintaxis con returns table.
Stephane Rolland
@StephaneRolland Actualizado con la explicación de la última parte de la pregunta también.
Craig Ringer
Gracias por el apoyo. Ahora es mucho más claro.
Stephane Rolland