Erreur: fonction set_valued appelée dans un contexte qui ne peut pas accepter un ensemble. De quoi s'agit-il?


11

J'utilise Postgresql 9.1, avec Ubuntu 12.04.

Inspiré par la réponse de Craig à ma question Enchaînement de type setof ou un enregistrement setof je pensais aller bien à l' utilisation return query, setof recordet un générateur de série dans cette fonction 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;

Pendant l'exécution, j'obtiens l'erreur:

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

Qu'est-ce qui ne va pas ? Contrairement à Craig, je dis à la fonction de revenir setof record.

Je peux réaliser quelque chose qui fonctionne exactement comme Craig, c'est-à-dire en définissant un type create type pair_id_value as (idx bigint, value integer)et que ma fonction plpgsql renvoie a setof of pair_id_valueau lieu de a setof record.

Mais même avec cette solution de travail, je ne comprends toujours pas pourquoi select id, generate_series(0,13)seul retournera un résultat dans deux colonnes ... et au contraire appeler la fonction (retourne setof pair_id_value) avec return query select id, generate_series(0,my_obj.value) from my_objretournera un résultat dans une seule colonne dont le champ ressemble ce "(123123,0)" "(123123,1)" "(123123,2)" (3 lignes) qui sont évidemment des tuples.

Est-ce un cas où une table temporaire doit / doit être créée?


Cela ne peut pas être le texte exact de la fonction que vous exécutez car elle ne se compile pas; il y a un point-virgule en excès après BEGINet un manquant après le RETURN QUERY. Après avoir corrigé ces erreurs, je confirme l'erreur lors du retour record; expliquera en réponse.
Craig Ringer

@CraigRinger J'ai remis le point-virgule en place.
Stephane Rolland

Réponses:


7

Le message d'erreur n'est pas très utile:

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

mais si vous reformulez la requête pour l'appeler comme une fonction de retour de jeu appropriée, vous verrez le vrai problème:

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 vous utilisez SETOF RECORDsans OUTliste de paramètres, vous devez spécifier les résultats dans l'instruction d'appel, par exemple:

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

Cependant, il est préférable d'utiliser des paramètres RETURNS TABLEou OUT. Avec l'ancienne syntaxe, votre fonction serait:

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;

Cela peut être appelé dans le contexte de la liste SELECT et peut être utilisé sans créer explicitement de type ni spécifier la structure des résultats sur le site d'appel.


En ce qui concerne la seconde moitié de la question, ce qui se passe, c'est que le premier cas spécifie deux colonnes distinctes dans une liste SELECT, tandis que la seconde renvoie un seul composite. Il ne s'agit en fait pas de la façon dont vous renvoyez le résultat, mais de la façon dont vous appelez la fonction. Si nous créons l'exemple de fonction:

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

Vous verrez la différence dans les deux façons d'appeler une fonction de retour d'ensemble - dans la SELECTliste, une extension non standard spécifique à PostgreSQL avec un comportement original:

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

ou sous forme de tableau de manière plus standard:

regress=> SELECT * FROM twocols();
 a | b 
---+---
 1 | 1
 2 | 2
 3 | 3
 4 | 4
 5 | 5
(5 rows)

Juste testé, fonctionne parfaitement. Et j'aime cette syntaxe avec returns table.
Stephane Rolland

@StephaneRolland Mise à jour avec explication de la dernière partie de la question également.
Craig Ringer

merci pour le support. Maintenant, c'est beaucoup plus clair.
Stephane Rolland
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.