Błąd: funkcja set_valued wywołana w kontekście, który nie może zaakceptować zestawu. O czym to jest?

11

Używam Postgresql 9.1, z Ubuntu 12.04.

Zainspirowany odpowiedź Craiga do mojego zapytania połączeniem setof typu lub setof rekordu myślałem, że go dobrze z użyciem return query, setof recordoraz generator serii do tej funkcji 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;

Podczas wykonywania pojawia się błąd:

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

Co jest nie tak ? W przeciwieństwie do Craiga mówię funkcji, aby powróciła setof record.

Mogę osiągnąć coś, co działa dokładnie tak, jak Craig, tj. Definiując typ create type pair_id_value as (idx bigint, value integer)i zwracając funkcję plpgsql setof of pair_id_valuezamiast a setof record.

Ale nawet z tym działającym rozwiązaniem nadal nie rozumiem, dlaczego select id, generate_series(0,13)sam zwróci wynik w dwóch kolumnach ... a wręcz przeciwnie, wywołanie funkcji (zwraca setof para_id_value) z return query select id, generate_series(0,my_obj.value) from my_objzwróci wynik tylko w jednej kolumnie, której pole wygląda jak ten „(123123,0)” „(123123,1)” „(123123,2)” (3 rzędy), które są oczywiście krotkami.

Czy jest to przypadek, w którym należy / należy utworzyć tymczasową tabelę?

Stephane Rolland
źródło
To nie może być dokładny tekst uruchomionej funkcji, ponieważ się nie kompiluje; występuje nadmiar średnika po BEGINi brakujący po RETURN QUERY. Po poprawieniu tych błędów potwierdzam błąd przy powrocie record; wyjaśni w odpowiedzi.
Craig Ringer
@CraigRinger Z powrotem umieściłem średnik.
Stephane Rolland

Odpowiedzi:

7

Komunikat o błędzie nie jest zbyt pomocny:

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

ale jeśli przepiszesz zapytanie w celu wywołania go jako właściwej funkcji zwracającej zestaw, zobaczysz prawdziwy problem:

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

Jeśli używasz SETOF RECORDbez OUTlisty parametrów, musisz określić wyniki w instrukcji wywołania, np .:

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

Jednak znacznie lepiej jest użyć RETURNS TABLElub OUTparametrów. Przy poprzedniej składni twoją funkcją byłoby:

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;

Jest to możliwe do wywołania w kontekście listy SELECT i może być używane bez jawnego tworzenia typu lub określania struktury wyników w witrynie wywołującej.


Jeśli chodzi o drugą połowę pytania, dzieje się tak, że pierwszy przypadek określa dwie oddzielne kolumny na liście WYBIERZ, a druga zwraca pojedynczy kompozyt. W rzeczywistości nie ma to związku z tym, jak zwracasz wynik, ale z tym, jak wywołujesz funkcję. Jeśli utworzymy przykładową funkcję:

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

Zobaczysz różnicę na dwa sposoby wywoływania funkcji zwracającej zestaw - na SELECTliście niestandardowe rozszerzenie PostgreSQL o dziwnym działaniu:

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

lub jako tabela w bardziej standardowy sposób:

regress=> SELECT * FROM twocols();
 a | b 
---+---
 1 | 1
 2 | 2
 3 | 3
 4 | 4
 5 | 5
(5 rows)
Craig Ringer
źródło
Właśnie przetestowane, działa idealnie. I podoba mi się ta składnia returns table.
Stephane Rolland
@StephaneRolland Zaktualizowano również z wyjaśnieniem drugiej części pytania.
Craig Ringer
dziękuję za wsparcie. Teraz jest o wiele wyraźniej.
Stephane Rolland