The approach you're using is unnecessarily complex - and very inefficient. Instead of the first function use:
create or replace function compute_pair_id_value(id bigint, value integer)
returns setof pair_id_value
as $$
SELECT $1, generate_series(0,$2);
$$
language sql;
or better, get rid of it entirely and write the whole operation like this:
-- Sample data creation:
CREATE TABLE my_obj(id bigint, obj_value integer);
insert into my_obj(id,obj_value) VALUES (1712437,2),(17000,5);
-- and the query:
SELECT id, generate_series(0,obj_value) FROM my_obj;
Resulting in:
regress=> SELECT id, generate_series(0,obj_value) FROM my_obj;
id | generate_series
---------+-----------------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
This exploits PostgreSQL's behaviour with set-returning functions called in the SELECT
list. Once PostgreSQL 9.3 comes out it can be replaced with a standards-compliant LATERAL
query.
Since it turns out your question was a simplified version of the real problem, let's tackle that. I'll work with the simplified compute_pair_id_value
above to avoid the hassle of plpython3. Here's how to do what you want:
SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
Result:
regress=> SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
but again, be warned that compute_pair_id_value
will be called more than once. This is a limitation of PostgreSQL's query executor that can be avoided in 9.3 with LATERAL
support, but as far as I know you're stuck with it in 9.2 and below. Observe:
create or replace function compute_pair_id_value(id bigint, value integer)
returns setof pair_id_value
as $$
BEGIN
RAISE NOTICE 'compute_pair_id_value(%,%)',id,value;
RETURN QUERY SELECT $1, generate_series(0,$2);
END;
$$
language plpgsql;
output:
regress=> SELECT (compute_pair_id_value(id,obj_value)).* FROM my_obj;
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(17000,5)
NOTICE: compute_pair_id_value(17000,5)
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
See how compute_pair_id_value
is called once per output column?
There is a workaround: Another layer of subquery to unpack the composite type result. See:
regress=> SELECT (val).* FROM (SELECT compute_pair_id_value(id,obj_value) FROM my_obj) x(val);
NOTICE: compute_pair_id_value(1712437,2)
NOTICE: compute_pair_id_value(17000,5)
id | value
---------+-------
1712437 | 0
1712437 | 1
1712437 | 2
17000 | 0
17000 | 1
17000 | 2
17000 | 3
17000 | 4
17000 | 5
(9 rows)
You can use the same technique in your code if you really must LOOP
over the results (it's slow to do that, so avoid it if you can).
Best Answer
PS: sorry for my comments, I was developing a complex code and I have a small error that seems a stupid PostgreSQL restriction on a "returing table" part... I was stupid, instead of concentrating and solving it, I used the internet (search engine put me here). Now, this wiki-answer is to help other readers, called by search engine and attracted by the title of the question.
Thanks to @dezso (was a correct answer) and, please all readers, you can edit this question to be more didactic, it is a Wiki.
Since PostgreSQL-v8 we can do it! We can RETURNS EXISTING_TABLE_NAME
In its Guide, in all PostgreSQL versions, from since pg v8 to current version, all have a section named "SQL Functions as Table Sources". Let's reproduce the Guide's example with some simplifications:
It is running as expected, it is perfect!
The question "How to use RETURNS TABLE with an existing table in PostgreSQL?" have a good answer since pg v8... This is the way we do it in the last 15 years, the syntax is:
RETURNS SETOF <EXISTING_TABLE_NAME>
.Use clause TABLE as instantaneous CREATE TABLE for returning
The @tinlyx's confusion, explainded on his question, is about the use of the clause
TABLE
instead ofSETOF
... To think using the "PostgreSQL syntax logic", we must first remember thatRETURN <EXISTING_TABLE_NAME>
is also valid, and it has the same behavior thatRETURN <EXISTING_TYPE_NAME>
. Is natural to return only one row.Next step, remember that we declare a tuple with the CREATE TABLE clause (<tuple_description>), therefore, a good syntax to express an "instant-define-tuple table" is RETURN TABLE (<tuple_description>), and it makes sense to return TABLE -type, which is like an array type, will return several instances (TABLE is a set of tuples).
Next step, remember that we declare a tuple with the
CREATE TABLE (<tuple_description>)
clause, therefore, a good syntax to express an "instantaneous table-definition" isRETURN TABLE (<tuple_description>)
; and it makes sense to return Table-type, that is like Array-type, they return multiple instances (TABLE is a set of tuples).The "modern thing" in PostgreSQL (!) is what @ZiggyCrueltyfreeZeitgeister showed, the
RETURNS TABLE (LIKE <table_name>)
syntax.Many ways to do the same, a summary:
Expliciting the table name (two ways) or type name:
RETURNS TABLE (LIKE <table_name>)
(modern and good)RETURNS SETOF <table_name>
(old but good)RETURNS SETOF <type_name>
(afterCREATE TYPE <type_name> (<tuple_description>)
)Implicit/generic ways, by anonymous types:
RETURNS SETOF RECORD
(generic but somethimes a problem)RETURNS SETOF ROW?
Instantaneous table-definition:
RETURNS TABLE (<tuple_description>)
RETURNS
) usingOUT
in the parameter list.For the last case, using our example to illustrate:
CREATE FUNCTION getfoo(int, OUT fooid int, OUT foosubid int, OUT fooname text)
For dynamic and/or polymophic input you must check this explanation.
Best practice?
There are many ways to do the same, so, there are a "best one"?
As syntax I prefer the use of
RETURNS TABLE (LIKE <table_name>)
, that is explicit: no confusion with "implicit RECORD", no fear of incompatibilities...Important for library management,
DROP TABLE foo CASCADE
will drop also the function: in any syntax (returns table
orreturns setof
) PostgreSQL will do a good job.