Ошибка: функция set_valued, вызываемая в контексте, которая не может принять набор. О чем это?

11

Я использую Postgresql 9.1, с Ubuntu 12.04.

Вдохновленный ответ Крейга на мой вопрос Конкатенацию setof типа или setof записи , я думал , я бы хорошо с использованием return query, setof recordи генератор серии в этой функцию 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;

Во время выполнения я получаю ошибку:

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

Что не так ? В отличие от Крейга, я говорю, чтобы функция возвращалась setof record.

Я могу добиться чего-то, что работает точно так же, как Крейг, то есть определив тип, create type pair_id_value as (idx bigint, value integer)и моя функция plpgsql возвращает a setof of pair_id_valueвместо a setof record.

Но даже с этим рабочим решением я все еще не понимаю, почему select id, generate_series(0,13)один вернет результат в двух столбцах ... и, наоборот, вызов функции (возвращает setof pair_id_value) return query select id, generate_series(0,my_obj.value) from my_objвернет результат только в одном столбце, поле которого выглядит как это "(123123,0)" "(123123,1)" "(123123,2)" (3 строки), которые, очевидно, являются кортежами.

Это тот случай, когда временная таблица должна быть создана?

Стефан Роллан
источник
Это не может быть точный текст функции, которую вы запускаете, потому что она не компилируется; есть лишняя точка с запятой после BEGINи пропущенная после RETURN QUERY. После исправления этих ошибок я подтверждаю ошибку при возврате record; объясню в ответ.
Крейг Рингер,
@CraigRinger Я вернул точку с запятой.
Стефан Роллан

Ответы:

7

Сообщение об ошибке не очень полезно:

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

но если вы перефразируете запрос, чтобы вызвать его как правильную функцию, возвращающую множество, вы увидите настоящую проблему:

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

Если вы используете SETOF RECORDбез OUTсписка параметров, вы должны указать результаты в операторе вызова, например:

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

Тем не менее, гораздо лучше использовать RETURNS TABLEили OUTпараметры. С прежним синтаксисом ваша функция будет:

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;

Это можно вызвать в контексте списка SELECT и можно использовать без явного создания типа или указания структуры результата на сайте вызова.


Что касается второй половины вопроса, то, что происходит, это то, что 1-й случай задает два отдельных столбца в SELECT-списке, тогда как второй возвращает один составной. На самом деле это не связано с тем, как вы возвращаете результат, а с тем, как вы вызываете функцию. Если мы создаем образец функции:

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

Вы увидите разницу в двух способах вызова функции, возвращающей множество - в SELECTсписке нестандартное расширение для PostgreSQL со странным поведением:

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

или в виде таблицы более стандартным способом:

regress=> SELECT * FROM twocols();
 a | b 
---+---
 1 | 1
 2 | 2
 3 | 3
 4 | 4
 5 | 5
(5 rows)
Крейг Рингер
источник
Только что протестировал, работает отлично. И мне нравится этот синтаксис с returns table.
Стефан Роллан
@StephaneRolland Обновлено с объяснением последней части вопроса тоже.
Крейг Рингер,
спасибо за поддержку. Теперь все намного понятнее.
Стефан Роллан