Erro: função set_valued chamada no contexto que não pode aceitar um conjunto. Sobre o que é isso?

11

Eu uso o Postgresql 9.1, com o ubuntu 12.04.

Inspirado pela resposta de Craig à minha pergunta concatenação do tipo setof ou registro setof eu pensei que eu iria bem com o uso return query, setof recorde um gerador de série para esta função 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 a execução, recebo o erro:

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

O que está errado ? Ao contrário de Craig, digo à função para retornar setof record.

Posso conseguir algo que funcione exatamente como Craig, ou seja, definindo um tipo create type pair_id_value as (idx bigint, value integer)e fazendo com que minha função plpgsql retorne a em setof of pair_id_valuevez de a setof record.

Mas mesmo com essa solução de trabalho, ainda não entendo por que select id, generate_series(0,13)sozinho retornará um resultado em duas colunas ... e, ao contrário, chamar a função (retorna setof pair_id_value) com return query select id, generate_series(0,my_obj.value) from my_objretornará um resultado em apenas uma coluna cujo campo se pareça com este "(123123,0)" "(123123,1)" "(123123,2)" (3 linhas) que são obviamente tuplas.

É um caso em que uma tabela temporária deve / deve ser criada?

Stephane Rolland
fonte
Esse não pode ser o texto exato da função que você está executando, porque não compila; há um excesso de ponto- BEGINe- vírgula após e um ausente após o RETURN QUERY. Depois de corrigir esses erros, confirmo o erro ao retornar record; irá explicar em resposta.
Craig Ringer
@CraigRinger Coloquei o ponto e vírgula de volta no lugar.
precisa

Respostas:

7

A mensagem de erro não é muito ú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);

mas se você reformular a consulta para chamá-la como uma função de retorno de conjunto adequada, verá o problema real:

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);

Se você estiver usando SETOF RECORDsem uma OUTlista de parâmetros, deverá especificar os resultados na instrução de chamada, por exemplo:

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

No entanto, é muito melhor usar RETURNS TABLEou OUTparâmetros. Com a sintaxe anterior, sua função seria:

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;

Isso pode ser chamado no contexto da lista SELECT e pode ser usado sem criar um tipo explicitamente ou especificar a estrutura de resultados no site da chamada.


Quanto à segunda metade da pergunta, o que está acontecendo é que o 1º caso especifica duas colunas separadas em uma lista SELECT, onde o segundo retorna um único composto. Na verdade, não tem a ver com o retorno do resultado, mas com a função. Se criarmos a função de amostra:

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

Você verá a diferença nas duas maneiras de chamar uma função de retorno de conjunto - na SELECTlista, uma extensão não padrão específica do PostgreSQL com comportamento peculiar:

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

ou como uma tabela da maneira mais padrão:

regress=> SELECT * FROM twocols();
 a | b 
---+---
 1 | 1
 2 | 2
 3 | 3
 4 | 4
 5 | 5
(5 rows)
Craig Ringer
fonte
Apenas testado, funciona perfeito. E eu gosto dessa sintaxe com returns table.
precisa
@StephaneRolland Atualizado também com a explicação da última parte da pergunta.
Craig Ringer
thx pelo suporte. Agora está muito mais claro.
precisa